继承和智能指针(std :: shared_ptr)

时间:2017-01-23 02:44:39

标签: c++ inheritance shared-ptr smart-pointers virtual-functions

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

value

这一切看起来都很好并且编译得很好,但是,下面的exame崩溃了。那是为什么?

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");
    }
};

2 个答案:

答案 0 :(得分:6)

Bar() : Foo(this) {}

this传递给shared_ptr时要小心。

考虑一下f.resetBar();ptr.reset(new Bar);之后会发生什么。

  1. 对于new Bar,将构造类型为Bar的对象,并将其构造函数this内部传递给父类成员ptr,然后对象由std::shared_ptr进行管理。

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

  3. 所以有两个std::shared_ptr指向同一个对象,但是std::shared_ptr s对此没有任何了解;因为你是单独构建它们的。当ff.ptr被销毁时,指向的对象也将被销毁。然后成员ptr将被销毁,它将再次尝试销毁同一个对象,从而导致UB。

    我不确定设计试图完成什么,但只是停止将this传递给std::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();
        }
    }
    

    IMO,让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();
        }
    }
    

答案 1 :(得分:0)

Foo::ptr拥有指向其母Foo(this)的指针,其引用次数为1.

Foo::resetBar(),当 Foo 要求 Foo :: ptr 致电reset(new Bar)时, Foo :: ptr 放弃了对其母亲 Foo (this)的所有权,并发现引用计数已降至0,因此需要杀死 Foo

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