【问题标题】:Destructor causes the program to crash析构函数导致程序崩溃
【发布时间】:2014-12-01 14:45:32
【问题描述】:

我真的很纠结将析构函数与复制构造函数结合使用的概念。如果我不使用析构函数,则代码可以正常工作,因为它会自动执行。如果我这样做,我会收到一条错误消息“调试断言失败!”和'表达式:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)。

但我希望能够了解如何使用析构函数。这是下面的代码,非常感谢帮助解释我做错了什么或需要做的事情!
类矩阵{

private:
    int M;
    int N;
    double *data;

public:
    Matrix();
    int getM() const { return M; }
    int getN() const { return N; }

    //CONSTRUCTOR
    Matrix(int sizeR, int sizeC,double * input_data)
    {
        M = sizeR; //Rows
        N = sizeC; //Columns

        data = new double[M*N]; //creation of 1D array, uses m&n values

        cout << "\nMatrix::Matrix(int sizeR, int sizeC, double * data_value) is invoked...\n\n";

        //ENTER DATA INTO MATRIX HERE:
        for(int i=0; i < M*N; i++) //Loops for every data entry into 1D array, uses r&c as referenece to
            data[i] = input_data[i];//Accesses each value at specific location, inputs value 'val'
        for(int i = 0; i < M*N; i++) //Loops for every data entry into 1D array, uses r&c as referenece to size
            cout << data[i] << " ";
    }

    //get function uses row and column from user
    double get(int i, int j)
    {
        return data[i*N+j];
    }

    double set(int i, int j, double val)
    {
        data[i*N+j] = val;

        cout << "\n\nNEW MATRIX: ";
        for(int i = 0; i < M*N; i++)//Loops for every data entry into 1D array, uses r&c as referenece to size
            cout << data[i] << " ";

        return val;
    }

    Matrix(const Matrix& oldMatrix)
    {
        cout¸<< "\nMatrix::Matrix(const Matrix&) is invoked....";
        M = oldMatrix.getM();
        N = oldMatrix.getN();
        data = oldMatrix.data;

        cout << "\n\n";

        //ENTER DATA INTO MATRIX HERE:

        for(int i = 0; i < M*N; i++)//Loops for every data entry into 1D array, uses r&c as referenece to size
            cout << data[i] << " ";

    }

    //DESTRUCTOR
    ~Matrix()
    {
        //delete data
        delete [] data;
        data = NULL;
        cout << "\n\nMatrix::~Matrix() is invoked...\n\n";
    }



};

int main()
{
    int sizeR, sizeC;
    double val;

    cout << "Enter No. Rows: ";
    cin >> sizeR;

    cout << "Enter No. Columns: ";
    cin >> sizeC;

    double * input_data;


    input_data = new double[sizeR*sizeC];

    //INPUTS VALUES TO ARRAY
    for(int i = 0; i < sizeR*sizeC; i++)//Loops for every row
        input_data[i] = i;

    Matrix M1(sizeR, sizeC, input_data);

    cout << "Enter row that value you are after is in: ";
    cin >> sizeR;
    cout << " & now the column that it is in: ";
    cin >> sizeC;


    cout << "Change value: " << M1.get(sizeR, sizeC) << " to:";
    cin >> val;
    M1.set(sizeR, sizeC, val);

    //calls copy constructor
    M1 = Matrix(M1);
}

【问题讨论】:

  • new[] 被调用了多少次? delete[] 被调用了多少次?
  • 在复制构造函数中设置data = oldMatrix.data1 看起来非常可疑!!!
  • 仅供参考,它是 destructor,而不是 deconstructor。我还对您的代码进行了一些缩进和放宽。不要害怕空白。
  • data 应该是vector&lt;double&gt;,不要使用原始指针

标签: c++ copy-constructor


【解决方案1】:

在复制构造函数中,您复制指针,这意味着您现在拥有两个具有相同指针的对象。如果其中一个对象被破坏,那么它会为另一个对象留下一个现在无效的指针。

无论如何解除对这个指针的引用,或者试图释放它,都会导致undefined behavior

有问题的行是这一行:

M1 = Matrix(M1);

该行创建一个 temporary 对象,并将数据从 M1 复制到该临时对象中,然后将临时对象分配回 M1(以及编译器生成的复制分配运算符只会对成员进行浅表复制,因此与您的复制构造函数没有太大区别)然后破坏临时对象,从而导致 M1 中的杂散和无效指针。


关于一个稍微相关的问题,您可能还想了解the rule of three

【讨论】:

  • 我可以建议零规则
【解决方案2】:

您正在复制构造函数中将一个对象的指针复制到另一个对象中:

Matrix(const Matrix& oldMatrix)
{
   ...
   data = oldMatrix.data;

调用复制构造函数后,您有两个对象引用同一个内存块。如果一个对象被销毁,则内存块被删除,第二个对象指向一个无效的内存位置。

在复制构造函数中你还需要分配一个新的缓冲区!

【讨论】:

    【解决方案3】:

    一种解决方案可能是在您的 Matrix 类中添加一个布尔变量(例如is_copy)。在构造函数上将其设置为 false,在复制构造函数上将其设置为 true。仅当 is_copy 为 false 时才在析构函数中释放内存。

    或者,按照 cmets 的建议,最好使用智能指针。

    【讨论】:

    • 这不是一个解决方案——它只会增加更多的问题。如果第一个对象被销毁,但副本仍然存在怎么办?您无法使用布尔标志解决所有权和间接问题。
    • 这就是我说“可能”的原因。有时您想要进行浅拷贝并且不能简单地通过引用传递矩阵。如果您的原始对象旨在在程序的整个执行过程中继续存在,那么没有问题
    • @NoelPerezGonzalez 一个智能指针可以解决这个问题,正如@WojtekSurowka 所说,布尔值不能解决它。 std::shared_ptrboost::shared_ptr。但是,我只会在数据为const 时使用这种共享方法。
    • @tillaert 你是对的。仍然有一些时候(例如 CUDA)你不能使用智能指针。但这无论如何都超出了问题的范围。
    • @NoelPerezGonzalez 我不认为应该使用这样的解决方案。在正常的 C++ 用法中,复制对象是安全的,或者不允许复制。您正在提出一种仅在特殊情况下有效的技巧,例如当某人自找麻烦时。添加一个返回对象的函数。此类技巧应仅在需要时使用,即使那样也不应按照您的建议隐藏。通过使用另一个函数而不是复制构造函数,或通过其他方式使缓冲区共享更加可见,使其显式化。
    猜你喜欢
    • 2011-06-12
    • 2013-04-13
    • 1970-01-01
    • 2017-06-28
    • 2015-04-29
    • 2013-07-04
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    相关资源
    最近更新 更多