以下代码降低了我的信念,即我或多或少地了解C ++。为什么valgrind
在这里没有显示任何memleak?为什么我期待memleaks:
B
大于A
:它包含一个额外的成员;所以在分配时应该有类'fields slicing。delete a
时,只应调用~A()
并且B中分配的内存将丢失。但我收到dtors的调用顺序是:~A(),~B(),~A()。为什么呢?!
struct A {
~A()
{
std::cerr << "~A" << std::endl;
}
};
struct B : A {
int* data;
B() : data(new int[20]) {}
~B()
{
std::cerr << "~B" << std::endl;
delete [] data;
}
};
main()
:
A* a = new A;
B* b = new B;
*a = *b;
delete a;
delete b;
UPD: 对我感到羞耻!当虚拟dtor应该被调用时,我混淆了基类指针删除对象。这里只是课堂复制的内容。谢谢大家!
答案 0 :(得分:5)
delete a;
a
为A*
,因此A::~A
被称为
delete b;
b
为B*
,因此B::~B
被调用。
为什么会出现问题?
类字段的切片?所以啊?您只是将A
字段从*b
复制到*a
,仅此而已。这里没有记忆丢失。
答案 1 :(得分:1)
在*a = *b
中,您正在*b
复制(是的,切片)到*a
。但是,这使*b
保持不变;它没有切片。当您delete b
时,您要求~B()
调用*b
。
答案 2 :(得分:1)
*a = *b;
作业仅复制数据的常见A
部分(A
没有字段)。
答案 3 :(得分:0)
你删除了每个新内容,所以没有memleak。
*a =*b
没有分配任何东西,复制构造函数只是复制b的一部分可以转到a,但它不会调用new ..
答案 4 :(得分:0)
通过调用*a = *b;
,您隐式调用编译器创建的赋值运算符函数,如下所示:
A &operator=( const A &other ){
}
在你的情况下没有做任何事情,因为A
没有成员变量。
答案 5 :(得分:0)
您的班级A
是空的,因此与本次讨论的目的完全无关。动态分配中涉及的唯一代码是:
B* b = new B;
delete b;
由于您delete
使用new
创建了一个对象,因此没有泄漏。此外,类B
包含足够的代码来清理其在这个直接用例中的内部分配。
(请注意,class B
仍然可怕地被打破,因为它无法在复制或分配中存活。经典的三法则(或五)。)
答案 6 :(得分:0)
因为你在这里做的是对象切片。我认为您打算让基指针指向派生对象,并且由于基本析构函数不是虚拟的,因此在清理期间不会调用派生类析构函数。但这不是这种情况。
*a = *b
编译器将“切片”对象的派生部分,并仅复制对象的基本部分。这通常不是我们想要做的,因为这会导致一个不完整的对象,只有基类属性和缺少专门的派生类属性。