当对象包含指针成员时,堆栈上的对象销毁出现分段错误

Segfault on object destruction on the stack when object containing pointer member?

本文关键字:对象 分段 错误 堆栈 指针 包含 成员      更新时间:2023-10-16

以下场景似乎非常令人困惑(这不是一个工作代码,只是一个非常简化的版本来说明该场景,你明白了):

class A {
private:
B* mb;
public:
A(B *b):mb(b) {}
~A() {
if(NULL != mb) { delete mb; }
}
}
int main(int argc, char **argv) {
B* b = new B();
A* a = new A(b);
delete A; //Everything is fine here
B b;
A* a = new A(&b);
//This will segfault, because b is allocated on the stack?
delete A; 
B b;
//This segfaults as well because when it goes out of scope
//the program tries to delete b twice?
A a(&b);
}

如果我正确理解这一点,这是否意味着当它们的类定义看起来像这样时,你再也不能在堆栈上分配像A和B这样的对象了?或者,我只是不为a定义析构函数,然后a和B都可以在堆栈上分配——但这可能很糟糕,可能会导致内存泄漏?(如果有一天其他人查看了代码并决定使用新的A)。

我认为C++相对于Java的主要优势是可以避免新的对象,并始终处理堆栈上的对象以加快速度,但像这样在堆栈或堆上使用对象的灵活性又如何呢?

C++有什么用?那么,以下两种方式中的哪一种应该走呢?

  1. 如上定义类A,并始终新建A和B,而以后请记住仅删除A。

  2. 定义不带析构函数的类A,然后总是在堆栈上创建A和B,并按值传递对象(但在某些情况下,我不想要公共复制构造函数)。

您假设这个segfault是正确的,因为您正在删除在堆栈上分配的项。您只能删除已用new分配的项目。此外,在同一层代码中分配和释放内存也是一种很好的做法。如果您必须在代码中使用对象B,最好创建B:的副本

class A {
private B* mb;
public:
A(B *b) {
mb = new B(*b); // calls B's copy constructor
}
~A() {
if(NULL != mb) { delete mb; }
}
}

那么应该采用以下两种方式中的哪一种?

两者都没有。使用选项3:根本不要这样做。你说得对,C++的一个优点是你不需要newdelete,但你写了一大堆使用newdelete的代码。

你想用这个A解决什么问题?毫无疑问,有比A更好的方法来解决这个问题。