C++ 将派生类与基类的std::shared_ptr一起使用

C++ 将派生类与基类的std::shared_ptr一起使用,c++,c++11,shared-ptr,c++14,C++,C++11,Shared Ptr,C++14,以下方法好吗 class TA { }; class TB : TA { }; std::shared_ptr<TA> spta; spta.reset(new TB); 类TA{}; TB类:TA{}; std::共享的ptr spta; 重置spta(新TB); 不,这不是因为TA是私人的 此外,正如注释中所建议的,基类的析构函数应该是虚拟的。这通常是一个很好的实践,因为您不能保证类的实例只与共享指针一起使用 要使其工作,您至少必须修改以下行: class TA

以下方法好吗

class TA      {  };
class TB : TA {  };

std::shared_ptr<TA> spta;
spta.reset(new TB);
类TA{};
TB类:TA{};
std::共享的ptr spta;
重置spta(新TB);

不,这不是因为
TA
是私人的

此外,正如注释中所建议的,基类的析构函数应该是虚拟的。这通常是一个很好的实践,因为您不能保证类的实例只与共享指针一起使用

要使其工作,您至少必须修改以下行:

class TA {  };
class TB : TA {  };
具体如下:

class TA { virtual ~TA() { } };
class TB : public TA {  };
这些都很好,因为下面的例子很好:


这主要取决于好对你意味着什么。至少是合法的。

显示的代码有一个问题,
TB
必须从
TA
公开继承。您有一个
shared\u ptr
,因此您要存储在其中的指针必须可转换为
TA
,但使用
private
继承时,基址不可访问,因此您的代码将无法编译

class TA             {  };
class TB : public TA {  };
除此之外,代码没有错误,并且表现良好。通常,当通过基类指针执行派生类实例的多态删除时,需要基类的析构函数为
virtual
,因此调用派生类析构函数,但在
shared_ptr
的情况下,这不是必需的。是一个函数模板,它将接受任何可转换为托管指针类型的
Y*
。同样的情况也适用于
共享\u ptr


也就是说,您应该更喜欢将基类的析构函数
设置为虚拟的
,尤其是当涉及的类具有其他
虚拟的
函数时。

这不是问题的答案,这是一次试图澄清任何关于shared_ptr看似神奇的能力的混淆,以避免使用虚拟析构函数

这里有一个小演示程序:

#include <iostream>
#include <memory>

struct A {
    ~A() { std::cout << __func__ << std::endl; }

    void foo() { do_foo(); }

protected:
    virtual void do_foo() {
        std::cout << "A::" << __func__ << std::endl;
    }
};

struct B : A {
    ~B() { std::cout << __func__ << std::endl; }

    virtual void do_foo() override {
        std::cout << "B::" << __func__ << " ";
        A::do_foo();
    }
};

using namespace std;

auto main() -> int
{
    std::shared_ptr<A> p = std::make_shared<A>();
    p->foo();
    p = std::make_unique<B>();
    p->foo();

    cout << "deleting B:" << endl;
    return 0;
}

请注意,在main()末尾销毁B时调用了正确的析构函数。

如果使用,则应首选
std::make_shared
。除此之外,它还包括“我们知道你住在哪里”优化。除此之外,只要你遵循Liskov的替换原则,基类是公共的,用一个指向基类的指针代替具体的实现绝对没有什么错,没有什么好处
TA
是一个私有基。空类是否需要虚拟析构函数?@LogicStuff不适用于
shared\u ptr
@TheBadger您是对的
std::make_unique
在C++14中是新的,但是在11版本中已经出现了
std::make_shared
。尽管如此,WKWYL优化是C++14带来的,而不是以前。无论如何,
std::make.*
是创建智能指针的首选方法。问题中的代码不对,它至少在某个地方遗漏了一个
public
关键字。是的,你是对的,遗漏了这一部分。但是不需要虚拟析构函数,尽管这是一个好主意,特别是当涉及的类有其他虚拟函数时。@RichardHodges不需要用于
共享\u ptr
,但无论如何这是一个好主意。@juanchopanza我不理解你的评论。它是一个
shared\u ptr
,而不是
shared\u ptr
,因此当共享指针释放底层对象时,它将调用
TA:~TA
,而不是
TB:~TB
。在这种特殊情况下,
TA
TB
都是空类,但我看不出
shared_ptr
如何使它变得无关紧要。为了简化示例,我假设这些类是空的,但只要
TB
有必须销毁的内容,这就很重要了。@juanchopanza感谢您指出这一点
shared_ptr
是一门设计精美的课程。我惊讶地发现,即使TA与
共享\u ptr
@合法化使用非空类时,即使TA具有虚拟函数且没有虚拟析构函数,clang(正确)也不会发出警告。@合法化添加了一个“答案”来演示我不确定为什么要在此处使用
使\u唯一
。但是,这似乎是可行的,因为
shared_ptr
存储了模板参数的关联删除程序。我使用make_unique来证明删除器已从中移出
#include <iostream>
#include <memory>

struct A {
    ~A() { std::cout << __func__ << std::endl; }

    void foo() { do_foo(); }

protected:
    virtual void do_foo() {
        std::cout << "A::" << __func__ << std::endl;
    }
};

struct B : A {
    ~B() { std::cout << __func__ << std::endl; }

    virtual void do_foo() override {
        std::cout << "B::" << __func__ << " ";
        A::do_foo();
    }
};

using namespace std;

auto main() -> int
{
    std::shared_ptr<A> p = std::make_shared<A>();
    p->foo();
    p = std::make_unique<B>();
    p->foo();

    cout << "deleting B:" << endl;
    return 0;
}
A::do_foo
~A
B::do_foo A::do_foo
deleting B:
~B
~A