Bootstrap

C++ - 深拷贝与浅拷贝 bitwise senimatics和memberwise senimatics

浅拷贝(bitwise senimatics):

如果类中拥有指针成员变量,而其指向堆中的一片区域,在赋值过程中,只是将指针的值进行了赋值,这样一来,这两个对象中的指针变量自然都是指向同一片内存区域了,即所谓的浅拷贝

#include <iostream>

using namespace std;

class Matrix
{
private:
    int _row, _col;
    double *_pmat;

public:
    // constructor
    Matrix(int row, int col) : _row(row), _col(col)
    {
        _pmat = new double[row * col];
    }

    bool insert(int i, int j, double val) {
        _pmat[i*_col+j] = val;
        return true;
    }

    double get(int i, int j) {
        return _pmat[i*_col+j];
    }
    
    // destructor
    // heap内存的自动管理
    ~Matrix()
    {
        delete[] _pmat;
        std::cout << "destrctor" << std::endl;
    }
};

int main() {
    int rol = 3, col = 3;
    Matrix mat = Matrix(3,3);
    for(int i = 0; i < rol; ++i) {
        for(int j = 0; j < col; ++j) {
            mat.insert(i, j, i*j);
        }
    }
    for(int i = 0; i < rol; ++i) {
        for(int j = 0; j < col; ++j) {
            std::cout << mat.get(i,j) << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "mat_copy: " << std::endl;
    Matrix mat_copy = mat;
    return 0;
}

Output:

0 0 0
0 1 2
0 2 4
mat_copy:
destrctor

我们发现会在析构mat_copy时运行出错,因为由于是浅拷贝,mat_copy._pmat和mat._pmat指向的是同一段地址,在第一次析构mat时,指向的堆内存已经被释放,因此第二次析构mat_copy时,再次释放同一段内存地址,便会造成运行错误。

深拷贝(memberwise senimatics):

所以这时就需要程序员自己来实现拷贝构造函数来完成那片堆内存的拷贝赋值操作,即所谓的深拷贝。

#include <iostream>

using namespace std;

class Matrix
{
private:
    int _row, _col;
    double *_pmat;

public:
    // constructor
    Matrix(int row, int col) : _row(row), _col(col)
    {
        _pmat = new double[row * col];
    }

    bool insert(int i, int j, double val) {
        _pmat[i*_col+j] = val;
        return true;
    }

    double get(int i, int j) {
        return _pmat[i*_col+j];
    }

    Matrix(const Matrix &);
    Matrix& operator=(const Matrix &);

    // destructor
    // heap内存的自动管理
    ~Matrix()
    {
        delete[] _pmat;
        std::cout << "destrctor" << std::endl;
    }
};

// 预防“成员逐一初始化”产生的错误,copy constructor
// 使某个对象的析构操作不至于影响到另一个对象。
Matrix::Matrix(const Matrix &rhs) : _row(rhs._row), _col(rhs._col)
{
    // 对rhs._pmat所指的数组产生一份完全复本。
    int elem_cnt = _row * _col;
    _pmat = new double[elem_cnt];

    for (int ix = 0; ix < elem_cnt; ++ix)
    {
        _pmat[ix] = rhs._pmat[ix];
    }
}

// Matrix需要一个copy constructor和一个copy assignment operator。
// 取代default memberwise copy,该做法并非exception-safe。
Matrix& Matrix::operator=(const Matrix &rhs)
{
    if(this != &rhs) {
        _row = rhs._row;
        _col = rhs._col;
        int elem_cnt = _row*_col;
        delete [] _pmat;
        _pmat = new double[elem_cnt];
        for(int ix = 0; ix < elem_cnt; ++ix)
            _pmat[ix] = rhs._pmat[ix];
    }
    return *this;
}

int main() {
    int rol = 3, col = 3;
    Matrix mat = Matrix(3,3);
    for(int i = 0; i < rol; ++i) {
        for(int j = 0; j < col; ++j) {
            mat.insert(i, j, i*j);
        }
    }
    for(int i = 0; i < rol; ++i) {
        for(int j = 0; j < col; ++j) {
            std::cout << mat.get(i,j) << " ";
        }
        std::cout << std::endl;
    }

    std::cout << "mat_copy: " << std::endl;
    Matrix mat_copy = mat;
    for(int i = 0; i < rol; ++i) {
        for(int j = 0; j < col; ++j) {
            std::cout << mat_copy.get(i,j) << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}

Output:

0 0 0
0 1 2
0 2 4
mat_copy:
0 0 0
0 1 2
0 2 4
destrctor
destrctor

;