C++ std::unique_ptr继承切片和析构函数
考虑以下功能完备的示例:C++ std::unique_ptr继承切片和析构函数,c++,c++11,C++,C++11,考虑以下功能完备的示例: #include <iostream> #include <memory> class A { public: A() { std::cout << "A() \n"; } ~A() { std::cout << "~A \n"; } }; class B:public A { public: B() {
#include <iostream>
#include <memory>
class A {
public:
A() {
std::cout << "A() \n";
}
~A() {
std::cout << "~A \n";
}
};
class B:public A {
public:
B() {
std::cout << "B() \n";
}
~B() {
std::cout << "~B() \n";
}
};
int main() {
std::cout << "Output: \n";
{
std::unique_ptr<A> TestB(new B());
}
return 0;
}
有没有办法用这样的继承调用B
的析构函数?我不知道unique_PTR也有切片问题。当然,我可以使用std::unique\u ptr
,但我想要一个std::vector
,并添加继承项
有没有一种方法可以将
std::unique_ptr
s列表与继承结合起来?当你说delete p
和包含*p
的最派生对象的类型(俗称“动态类型的*p
”)与*p
的静态类型不同,如果*p
的静态类型是类类型并且没有虚拟析构函数,则行为是未定义的
要解决这个问题,您需要说
virtual~A()
@user2384250的真正问题似乎是为什么虚拟分派不是默认值
TLDR:您将提前支付性能损失(在调用站点,由于破坏缓存位置而创建的每个实例和程序范围内的实例)。如果默认情况下所有函数都执行虚拟分派,那么这将是一个无法收回的惩罚(没有更糟糕的语法)
如果您在类中的任何位置都不使用虚拟分派,那么您的类将具有尽可能最好的性能。即使B从A继承,如果A没有任何虚拟方法,编译器也无法区分B和A的实例;如果您有一个变量a*实例代码>&你调用实例->foo()
,编译器无法知道你下面有一个B&它将调用a::foo()
在A中声明foo()
virtual
时,编译器将为A创建一个虚拟表,将foo()
插入该虚拟表并添加指向该类的隐藏虚拟表指针。然后,在每次调用foo()时,它都知道需要执行虚拟分派(因为foo()被声明为虚拟的)。它将加载指针给出的查找表&调用它在那里被告知的foo()。这样,当您有一个B实例时,指针将指向B类的查找表&当您有一个A实例时,它将指向A实例;因此,无论instance
是A*还是B*,编译器都将只加载查找表并调用调度表中的foo,而不管调用站点上声明的类型如何
如您所见,即使添加一个虚拟方法,也会有一个独立于调用虚拟方法的隐藏开销;每个类将得到1个查找表&类的每个实例都将增加1个指针。此外,编译器无法提前知道是否要创建子类(虚拟表指针位于您第一次声明虚拟方法的类中)。如果您希望默认行为是虚拟分派,那么程序中的每个类都将不必要地支付此性能惩罚
此外,由于上述机制,虚拟方法的成本稍高一些:编译器没有插入指令:跳转到函数foo(),而是必须:加载此实例的虚拟指针,为函数foo()添加偏移量,取消引用该项(函数的地址)并跳转到它。这不仅会占用更多的CPU周期,还会破坏缓存位置
最后,您应该真正考虑继承、组合还是模板是解决问题的更好方法;每个都有折衷。你需要使~A()
虚拟
@juanchopanza我也在想同样的事情,这是正确的答案,谢谢。UB没有虚拟析构函数…这是真的,但我不明白为什么,我的意思是,如果我声明一个对象bk
,其中B
是从A
派生出来的,那么为什么调用A
的析构函数是“自然”的呢?这里有一个明显的优先级,或者可能是因为C++支持多重继承…@ USER884250:与这些无关。问题是您的类型是唯一的\u ptr
。如果有一个唯一的\u ptr
就可以了。也许我没有正确地解释我自己,我正在考虑我刚刚声明bk代码>在问题帖子中定义的类,我不能解释它是如何用C++来设计这种方式以及它是如何操作它的行为的,当我声明“代码> B K<代码>代码> k>代码>被破坏时,<代码> ~A < /C>也被调用,这是我真正不能得到的部分。如果你只声明<代码> BK;<代码>,没有问题。只有当您试图通过指向基子对象的指针销毁动态分配的对象时,才会出现问题,原因很明显:子对象必须有某种机制知道它是较大对象的一部分。我知道这没有问题,我的问题是Bjarne S.在设计这个的时候是怎么想的,我也是这么看的,B比A大,所以在类型B的对象上调用2析构函数~B和~A对我来说毫无意义,析构函数~A能做什么~B不能做,因为B是比A大的类型?
Output:
A()
B()
~A