unique_ptr调用reset后没有删除

unique_ptr is not deleted after calling reset

这是我最小的、可重现的例子

#include <memory>
#include <chrono>
#include <thread>
#include <iostream>
#include <functional>


class BaseClass {
public:
    void do_func() {
        while(true) {
            std::cout << "doing stuff" << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        
    }
};

int main() {
    auto obj = std::make_unique<BaseClass>();
    std::thread t(&BaseClass::do_func, obj.get());
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "reset called!" << std::endl;
    obj.reset();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "going out of scope" << std::endl;
    t.join();
    return 0;
}

我原以为对象会在调用重置后被删除。连代码都不能退出,因为while循环阻塞了,这是可以理解的。我需要在特定事件后删除对象,不能等到 unique_ptr 超出范围。如果我将 do_func 更改为

void do_func() {
    std::cout << "doing stuff" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(20));
}

那么这是预期的行为。

编辑: 根据您的意见,我已将代码更新为

#include <memory>
#include <chrono>
#include <thread>
#include <iostream>
#include <functional>


class BaseClass {
public:

    BaseClass() : x(1) {
        dummy = std::make_shared<SomeClass>();
    }

    void do_func() {
        while(true) {
            std::cout << "doing stuff " << dummy->do_stuff(x) << std::endl;
            x++;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
private:
    int x;
    class SomeClass {
    public:
        int do_stuff(int x) {
            return x * x;
        }
    }; 
    std::shared_ptr<SomeClass> dummy;
};

int main() {
    auto obj = std::make_unique<BaseClass>();
    std::thread t(&BaseClass::do_func, obj.get());
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "reset called!" << std::endl;
    obj.reset();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "going out of scope" << std::endl;
    t.join();
    return 0;
}

现在函数确实打印垃圾值。这是否意味着我需要在析构函数中显式删除 dummy

感谢您的快速回复!让我知道这是否是一个好的解决方案

#include <memory>
#include <chrono>
#include <thread>
#include <iostream>
#include <functional>


class BaseClass {
public:

    BaseClass() : x(1) {
        dummy = std::make_shared<SomeClass>();
    }

    virtual ~BaseClass() {
        dummy.reset();
    }

    void do_func() {
        while(dummy) {
            std::cout << "doing stuff " << dummy->do_stuff(x) << std::endl;
            x++;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
private:
    int x;
    class SomeClass {
    public:
        int do_stuff(int x) {
            return x * x;
        }
    }; 
    std::shared_ptr<SomeClass> dummy;
};

class DerivedClass : public BaseClass {

};

int main() {
    auto obj = std::make_unique<DerivedClass>();
    std::thread t(&BaseClass::do_func, obj.get());
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "reset called!" << std::endl;
    obj.reset();
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "going out of scope" << std::endl;
    t.join();
    return 0;
}

现在的行为符合预期。

同步这两个线程的最简单方法是使用std::atomic_bool

#include <atomic>

class BaseClass {
public:
    std::atomic_bool shouldContinueWork = true;

    void do_func() {
        while(shouldContinueWork) {
            std::cout << "doing stuff" << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }

    }
};

int main() {
    auto obj = std::make_unique<BaseClass>();
    std::thread t(&BaseClass::do_func, obj.get());
    std::this_thread::sleep_for(std::chrono::seconds(5));
    obj->shouldContinueWork = false; //the thread will not do anything more after this, but the sleep will need to end on it's own
    std::cout << "stopping work!" << std::endl;

    // do not remove the object before join is called - you don't know if it will be still accessed from the other thread or not
    // obj.reset();

    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "going out of scope" << std::endl;
    t.join();
    // here it is safe to remove the `obj`, main thread is surely the only thread that accesses it
    // (but it goes out of scope anyway)
    return 0;
}

这个解决方案没有考虑中途停止工作(即必须始终执行整个循环迭代)并且通常容易出现或多或少的工作迭代 - 当你有时它应该足够精确睡眠时间为 1 秒,但睡眠时间越短,它不能保证任何确切的迭代次数,请考虑到这一点。 std::condition_variable可用于更精确的线程同步控制。