C++ C++;如何知道某个对象何时被销毁

C++ C++;如何知道某个对象何时被销毁,c++,c++98,C++,C++98,我不明白当一个死对象是从另一个作用域创建的,并且被放在另一个作用域中的某种容器中时,我如何阻止访问它。范例 #include <iostream> #include <string> #include <vector> class Foo { public: void Speak(){ std::cout << "ruff"; } }; int main

我不明白当一个死对象是从另一个作用域创建的,并且被放在另一个作用域中的某种容器中时,我如何阻止访问它。范例

#include <iostream>
#include <string>
#include <vector>

class Foo {
    public:
        void Speak(){
            std::cout << "ruff";    
        }
};

int main()
{
    std::vector<Foo*> foos;
    
    {
        Foo foo;
        foos.push_back(&foo);
        // foo is now dead 
    }
    
    for(size_t i = 0; i < foos.size(); i++){
        // uh oh
        foos[i]->Speak();   
    }
}
如果我们有一个名为
MyObject
的组件,其中包含一个我们想要监听的
EventEmitter
,情况会变得更糟。如果
MyObject
被复制,现在我们有了这个内部
EventEmitter
,其中的处理程序引用了可能不再存在的
MyObject

所以我可能会把它全部扔掉,转而使用回调。。但即使如此,某些对象仍然拥有可能不再存在的PTR或对某些其他对象的引用!我们可以尽可能小心,但我们永远不知道会发生什么

我想我需要说的是

  • 任何对象都不应该有对另一个对象的引用或ptr,除非它自己创建了该对象
  • 现在,必须通过管理这两个对象的更高级别的对象将对象链接在一起


    …我应该坚持平面设计。

    你为什么不使用
    std::vector
    而不是
    std::vector
    ?在前一种方式中,我认为没有办法阻止 FoO 对象在范围之外时被销毁——但是当你推到 vector 时,它会一直坐在那里直到程序终止或我们显式删除。C++中的

    < P>你对对象的生命周期负责。如果你使用

    Foo foo;
    
    foo将在超出范围时销毁。 所以,不应该使用局部变量。 使用动态对象(例如):

    它会起作用的。
    你要负责销毁对象。

    如果你想让对象的生存期超出创建对象的范围,那么必须使用动态生存期来创建该对象。您可以使用
    new
    执行此操作:

    std::vector<Foo*> foos;
    
    {
        Foo* foo = new Foo;
        foos.push_back(foo);
    }
    

    理想情况下,您可以使用某种类型的智能指针来管理动态生命周期对象,但C++98中没有标准的智能指针
    std::unique_ptr
    std::shared_ptr
    std::auto_ptr
    可用,但该类很容易被错误使用。为您编写一个简单的
    共享类可能是值得的。如果您不需要支持弱指针和原子操作,那么它就不会太复杂。下面是一个非常基本的实现:

    template <typename T>
    class shared_ptr
    {
    private:
        struct control_block
        {
            control_block(T* ptr)
                : ref_count_(1),
                  ptr_(ptr)
            {}
            
            ~control_block()
            {
                delete ptr_;
            }
            
            size_t ref_count_;
            T* ptr_;
        };
    
        control_block* control_block_;
        
    public:
        shared_ptr()
            : control_block_(NULL)
        {}
    
        shared_ptr(T* ptr)
            : control_block_(new control_block(ptr))
        {}
        
        shared_ptr(const shared_ptr& other)
            : control_block_(other.control_block_)
        {
            ++control_block_->ref_count_;
        }
        
        shared_ptr& operator=(shared_ptr other)
        {
            std::swap(control_block_, other.control_block_);
            return *this;
        }
        
        ~shared_ptr()
        {
            if (control_block_) {
                --control_block_->ref_count_;
                if (control_block_->ref_count_ == 0) {
                    delete control_block_;
                }
            }
        }
        
        T& operator*() { return *control_block_->ptr_; }
        T* operator->() { return control_block_->ptr_; }
        
        bool operator==(const shared_ptr& other)
        {
            return control_block_ == other.control_block_;
        }
    };
    
    模板
    类共享\u ptr
    {
    私人:
    结构控制块
    {
    控制块(T*ptr)
    :参考计数(1),
    ptr(ptr)
    {}
    ~control_block()
    {
    删除ptr;
    }
    尺寸参考计数;
    T*ptr;
    };
    控制块*控制块;
    公众:
    共享_ptr()
    :控制块(空)
    {}
    共享ptr(T*ptr)
    :控制块(新控制块(ptr))
    {}
    共享\u ptr(常数共享\u ptr和其他)
    :控制块(其他控制块)
    {
    ++控制块->参考计数;
    }
    共享\u ptr&运算符=(共享\u ptr其他)
    {
    标准::交换(控制块、其他控制块);
    归还*这个;
    }
    ~shared_ptr()
    {
    如果(控制块){
    --控制块->参考计数;
    如果(控制块->参考计数=0){
    删除控制块;
    }
    }
    }
    T&运算符*(){return*控制块->ptr}
    T*运算符->(){返回控制块->ptr;}
    布尔运算符==(常数共享\u ptr和其他)
    {
    返回控件\u块==其他控件\u块;
    }
    };
    


    基本前提是给定对象的所有
    shared_ptr
    s都持有指向相同
    控制块的指针。每当复制
    共享ptr
    时,其
    控制块
    的参考计数将增加;每当
    共享ptr
    被销毁时,其
    控制块
    的参考计数将减少。如果引用计数曾经达到零,它将删除控制块以及指向的对象。

    您可以使用将向量赋给foo本身并删除析构函数中的指针。基本上,对象本身负责记录指针

    #include <iostream>
    #include <string>
    #include <vector>
    
    class Foo {
        std::vector <Foo *> *v;
        public:
        Foo(std::vector <Foo *> *v):  v(v){}
        void Speak(){
            std::cout << "ruff";    
        }
        ~Foo() {
            auto it = std::find(v->begin(), v->end(), this);
            if (it != v->end())
            {
                v->erase(it);
            }
        }
    };
    
    int main()
    {
        std::vector<Foo*> foos;
        
        {
            Foo foo(&foos);
            foos.push_back(&foo);
            // foo is now dead 
        }
        std::cout << "length is " << foos.size() << '\n';
        for(size_t i = 0; i < foos.size(); i++){
            // uh oh
            foos[i]->Speak();   
        }
    }
    

    我想把我自己的答案放在这里,展示我在不知不觉中想要写的东西:垃圾收集。更重要的是,在已经自动管理的内存上

    这花了大约3天的时间才理解。我正在创建一个节点和引用系统,以使对象保持活动状态。垃圾收集


    Woopsies.

    但这不一样,因为对象会被复制到向量中
    Foo
    可能没有复制构造函数,或者复制它可能会产生不必要的副作用。(是的,这段代码中没有,但这只是示例代码…)此外,在推送后修改
    foo
    ,也不会在向量中更改它的新实例。对于
    std::move
    它可以这样工作,但这不是C++98。如果
    Foo
    是一个非常复杂的对象,该怎么办?如果复制它的行为有如此多的副作用,我必须保持。我说像vector这样的容器应该是1,对吗。只有在堆(新)2上创建对象时才有PTR。否则,它将对其对象进行100%的管理。@ony_pox232——您关心的问题实际上与
    vector
    无关。一个简单的
    Foo数组
    会引起与您提到的相同的问题。
    它不太复杂
    -好的,我觉得我几乎可以写任何东西。但是有什么地方我可以得到共享PTR的概要吗?他们使用什么机制?@ony_pox232添加了一个基本示例,并解释了前提
    std::shared_ptr
    有一些额外的特性和优化,但它使用相同的基本前提
    for (std::size_t i = 0; i < foos.size(); ++i) {
        delete foos[i];
    }
    
    template <typename T>
    class shared_ptr
    {
    private:
        struct control_block
        {
            control_block(T* ptr)
                : ref_count_(1),
                  ptr_(ptr)
            {}
            
            ~control_block()
            {
                delete ptr_;
            }
            
            size_t ref_count_;
            T* ptr_;
        };
    
        control_block* control_block_;
        
    public:
        shared_ptr()
            : control_block_(NULL)
        {}
    
        shared_ptr(T* ptr)
            : control_block_(new control_block(ptr))
        {}
        
        shared_ptr(const shared_ptr& other)
            : control_block_(other.control_block_)
        {
            ++control_block_->ref_count_;
        }
        
        shared_ptr& operator=(shared_ptr other)
        {
            std::swap(control_block_, other.control_block_);
            return *this;
        }
        
        ~shared_ptr()
        {
            if (control_block_) {
                --control_block_->ref_count_;
                if (control_block_->ref_count_ == 0) {
                    delete control_block_;
                }
            }
        }
        
        T& operator*() { return *control_block_->ptr_; }
        T* operator->() { return control_block_->ptr_; }
        
        bool operator==(const shared_ptr& other)
        {
            return control_block_ == other.control_block_;
        }
    };
    
    #include <iostream>
    #include <string>
    #include <vector>
    
    class Foo {
        std::vector <Foo *> *v;
        public:
        Foo(std::vector <Foo *> *v):  v(v){}
        void Speak(){
            std::cout << "ruff";    
        }
        ~Foo() {
            auto it = std::find(v->begin(), v->end(), this);
            if (it != v->end())
            {
                v->erase(it);
            }
        }
    };
    
    int main()
    {
        std::vector<Foo*> foos;
        
        {
            Foo foo(&foos);
            foos.push_back(&foo);
            // foo is now dead 
        }
        std::cout << "length is " << foos.size() << '\n';
        for(size_t i = 0; i < foos.size(); i++){
            // uh oh
            foos[i]->Speak();   
        }
    }
    
    ➜  test ./a.out
    length is 0