2017-01-23 130 views
1

有很多事情需要说。首先,我想知道下面的方法是否被认为是一种设计模式,甚至是一种常见的技术(这就是为什么我没有提供有关标题的更多信息)。如果是这样的话,那叫什么名字?无论如何,这是我试图实现的缩小版本。由于我需要使用复制,我发现使用std :: shared_ptr最好避免释放(删除)指针。继承和智能指针(std :: shared_ptr)

class Foo 
{ 
public: 
    Foo() : ptr(nullptr) {} 
    Foo(const Foo& foo) : ptr(foo.ptr) {} 
    virtual ~Foo() = default; 

    void whatever() { 
     if (ptr) 
      ptr->whateverHandler(); 
    } 

    void reset() { 
     ptr.reset(); 
    } 

    void resetBar() { 
     ptr.reset(new Bar); 
    } 

    // Other resets here... 

protected: 
    Foo(Foo* foo) : ptr(foo) {} 

private: 
    // Every child class should override this 
    virtual void whateverHandler() { 
     throw "whateverHandler cant be called within base class"; 
    } 

protected: 
    std::shared_ptr<Foo> ptr; 
}; 

class Bar : public Foo 
{ 
public: 
    Bar() : Foo(this) {} 
    void whateverHandler() { 
     printf("Bar's handler!!! \n"); 
    } 
}; 

这一切看起来不错,编译好,但是,下面的exame崩溃。这是为什么?

int main() 
{ 
    { 
     Foo f; 

     f.resetBar(); 
    } 

    return getchar(); 
} 
+0

当一个Bar被销毁时,它的Foo被销毁两次 – Danh

+0

你可能需要'std :: enable_shared_from_this'。但是,在这个特定的例子中,你不需要它,要么 – Danh

+1

你也错过了虚拟析构函数。 –

回答

6
Bar() : Foo(this) {} 

当你通过thisshared_ptr要小心。

想想在f.resetBar();ptr.reset(new Bar);之后会发生什么。

  1. 对于new BarBar类型的对象将被构造,并且它的构造内this被传递到父类构件ptr,那么该目的通过其是std::shared_ptr的它进行管理。

  2. 之后,该对象由f.ptr管理;这是另一个std::shared_ptr

所以这里有两种std::shared_ptr指点下,以相同的对象,但std::shared_ptr■不要知道呢;因为你在分别构建它们。当ff.ptr被销毁时,指向的对象也会被销毁。然后成员ptr将被销毁,它会尝试再次销毁同一个对象,从而导致UB。

我不知道什么设计试图完成,但只是停止传递thisstd::shared_ptr可以消除UB。

class Foo 
{ 
public: 
    virtual ~Foo() = default; 
    void whatever() { 
     if (ptr) 
      ptr->whateverHandler(); 
    } 
    void reset() { 
     ptr.reset(); 
    } 
    void resetBar() { 
     ptr.reset(new Bar); 
    } 
    // Other resets here... 
private: 
    // Every child class should override this 
    virtual void whateverHandler() = 0; 
    std::shared_ptr<Foo> ptr; 
}; 

class Bar : public Foo 
{ 
public: 
    void whateverHandler() { 
     printf("Bar's handler!!! \n"); 
    } 
}; 

int main() 
{ 
    { 
     Foo f; 
     f.resetBar(); 
     f.whatever(); 
     f.resetSthElse(); 
     f.whatever(); 
    } 
} 

和国际海事组织,具有std::shared_ptr类型的成员指向派生类是混乱;分开它可能会更好。然后,我认为它可能是bridge design partern

class Foo 
{ 
public: 
    void whatever() { 
     if (ptr) 
      ptr->whateverHandler(); 
    } 
    void reset() { 
     ptr.reset(); 
    } 
    void resetBar() { 
     ptr.reset(new BarHandler); 
    } 
    // Other resets here... 
private: 
    std::shared_ptr<FooHandler> ptr; 
}; 

class FooHandler 
{ 
public: 
    virtual ~FooHandler() = default; 
    // Every child class should override this 
    virtual void whateverHandler() = 0; 
}; 

class BarHandler : public FooHandler 
{ 
public: 
    void whateverHandler() { 
     printf("Bar's handler!!! \n"); 
    } 
}; 

int main() 
{ 
    { 
     Foo f; 
     f.resetBar(); 
     f.whatever(); 
     f.resetSthElse(); 
     f.whatever(); 
    } 
} 
+0

我已经稍微改变了我的帖子,以配合真正的问题(旧的错误陈述,你回答了它)。但是为什么'd.resetFree()'会崩溃? –

+0

@YvesHenri没有任何改变。对于'f.resetBar();'和'ptr.reset(new Bar);','f.ptr'会指向它自己的父成员'ptr'指向的构造对象。请注意,有两个'Foo'和'Bar'类型的对象,都是成员'ptr'指向同一个对象。 – songyuanyao

+0

那么我怎样才能实现我在找的东西呢?我选择智能指针的原因是因为我需要在拷贝上传递基本的内部指针,但是我需要安全地检测它是否被删除。只需将其更改为原始指针并执行释放操作,然后您将拥有旧版本(我尝试过)。要清楚,假设你已经声明了2条,但是通过复制构造函数(检索内部指针)创建了一条,但是你销毁了复制的条(例如超出了范围)。现在这个副本会在下一个'whatever'调用中崩溃! :( –

0

Foo::ptr具有指向其母亲Foo(this)参照计数1

Foo::resetBar(),当富:: PTR调用reset(new Bar)富:: PTR了将其所有权转移给其母亲Foo(this)并发现引用计数已降至0,因此需要杀死Foo

Foo死亡时,其子女被杀害。所以Foo :: ptr也必须是死的。然后将new Bar分配给死亡Foo::ptr会导致UB。