使用指针和析构函数的c++代码设计

Crash Issue - C++ Code Design using pointers and destructors

本文关键字:c++ 代码 析构函数 指针      更新时间:2023-10-16

我有一个关于以下代码的问题,它会崩溃。我在testfunction()中创建了一个局部变量,然后将它(变量"y")推入列表。这个变量有一个对象类型为Ball的成员指针b。据我所知,这个局部变量"y"位于堆栈上,因此它的析构函数将在testfunction()完成后被调用。此外,据我所知,向量将对象"复制"到它的列表中。从我所学到的,最好的做法是在析构函数中删除指针,如果一个指针存在于它的类中。因此,在Example的析构函数中有"delete b"。

我遇到的问题是对象y.b在testfunction()完成时被销毁。在main()中,我可以看到"name"的值和"b"的地址,但是对象"b"已经被删除了。我想避免这种情况。

我认为代码的设计/指针与引用的使用等存在问题。请指引我正确的方向,我是个白痴!

#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Ball
{
    public:
        int a;
        Ball()
        {
            a = 0;
        }
        ~Ball()
        {
            cout << "destroyed Ball()" << endl;
        }
};
class Example
{
    public:
        string name;
        Ball* b;
        Example()
        {
            name = "";
            b = NULL;
        }
        ~Example()
        {
            cout << "destroying Example()" << endl; 
            delete b;
        }
};
void testfunction(vector<Example>& list)
{
    cout << "entered testfunction1()" << endl;
    Example y;
    y.name = "myName";
    y.b = new Ball();
    y.b->a = 5;
    cout << "y.b->a = " << y.b->a << endl;
    list.push_back(y);
    cout << "exit testfunction1()" << endl;
}
void testfunction2()
{
    cout << "entered testfunction2()" << endl;
    Example* y = new Example();
    cout << "exit testfunction2()" << endl;
}
int main() {
    vector<Example> list;
    testfunction(list);
    //testfunction2();
    if(list[0].b == NULL)
        cout << "b is null" << endl;
    else
        cout << "b is not null" << endl;
    cout << list[0].name << endl;
    cout << list[0].b << endl;
    cout << "list[0].b->a = " << list[0].b->a << endl;
    return 0;
}

由于class Example有一个指针成员,并且它试图拥有一个动态分配的资源,所以它需要非默认的复制操作,也就是说,它需要用户自定义的复制构造函数和赋值操作符。

testfunction中,当您将y复制到vector时,本地yy copied to the vector都指向同一个Ball对象。本地y在函数结束时被销毁,Ball被删除。但是,删除后的Ball仍然指向y in vector

void testfunction(vector<Example>& list)
{
    // ...
    Example y;
    y.name = "myName";
    y.b = new Ball();
    y.b->a = 5;
    list.push_back(y);
    // ...
} // <-- destructor for Example y is called and y.b is deleted

为类Example定义复制构造函数和赋值操作符。这些将正确地复制你的对象(创建一个复制的球对象),当推回矢量。

Example(const Example& a)  
{
    name = a.name;  // attention no dynamic allocation
    cout << "copy" <<endl; 
    if (a.b) {
        b = new Ball(*a.b);   // create a new duplicated Ball 
    }
    else b = NULL; 
}

示例中的问题是,当您推回对象时调用默认复制构造函数。它按成员进行复制,因此复制的是指向Ball的指针,而不是所指向的对象。

另一种选择可能是将Ball*替换为shared_ptr<Ball>(相应地,将new Ball替换为make_shared<Ball>(),将对象的delete b替换为b.reset())。原理是,这个智能指针跟踪对象被使用的时间,所以它不会删除它两次,但只有当它不再使用任何地方。