如何正确实现具有原始指针的 class 的复制构造函数?

How to properly implement a copy constructor of a class that has raw pointers?

我有一个 C++ 对象,我通过 extern "c" 作为指针导出。为了能够创建对象并 return 它,我必须在堆上进行,考虑到尝试 return 局部范围变量的范围问题。但是,为了在堆上分配,我必须能够复制一个对象,我正在努力做到这一点。这是一个例子:

#include <iostream>
using namespace std;

// used to simulate complex data structure in nex object
class DataStorage {
private:
    int a;
public:
    explicit DataStorage(int a) : a(a) {

    }
};

// object for export "C". Contains some pointers. 
class NonTrivialObject {
public:
    int *int_ptr;
    char *char_ptr;
    double *double_ptr;
    DataStorage *data_storage_ptr;
    NonTrivialObject(int *int_ptr, char *char_ptr, double *double_ptr, DataStorage *data_storage_ptr)
            : int_ptr(int_ptr), char_ptr(char_ptr), double_ptr(double_ptr), data_storage_ptr(data_storage_ptr) {}

    ~NonTrivialObject() {
        /*
         * only delete objects of allocated on heap
         */

    }

    /*
     * Copy constructor
     */
    NonTrivialObject(const NonTrivialObject &rhs) {
        if (this != &rhs) {
            this->int_ptr = rhs.int_ptr;
            this->char_ptr = rhs.char_ptr;
            this->double_ptr = rhs.double_ptr;
            this->data_storage_ptr = rhs.data_storage_ptr;
        }
    }

    /*
     * Copy assignment constructor
     */
    NonTrivialObject &operator=(const NonTrivialObject &rhs) {
        if (this != &rhs) {
            this->int_ptr = rhs.int_ptr;
            this->char_ptr = rhs.char_ptr;
            this->double_ptr = rhs.double_ptr;
            this->data_storage_ptr = rhs.data_storage_ptr;
        }
        return *this;
    }

    /*
     * Move assignment constructor
     */
    NonTrivialObject &operator=(NonTrivialObject &&rhs) noexcept {
        if (this != &rhs) {
            this->int_ptr = rhs.int_ptr;
            this->char_ptr = rhs.char_ptr;
            this->double_ptr = rhs.double_ptr;
            this->data_storage_ptr = rhs.data_storage_ptr;
        }
        return *this;
    }

    /*
     * Move constructor
     */
    NonTrivialObject(NonTrivialObject &&rhs) noexcept {
        if (this != &rhs) {
            this->int_ptr = rhs.int_ptr;
            this->char_ptr = rhs.char_ptr;
            this->double_ptr = rhs.double_ptr;
            this->data_storage_ptr = rhs.data_storage_ptr;
        }
    }
};

extern "C" {

// first method will not work because nonTrivialObjectPtr is locally scoped
NonTrivialObject *CopyANonTrivialObject1(NonTrivialObject obj) {
    auto *nonTrivialObjectPtr = (NonTrivialObject *) malloc(sizeof(obj));
    nonTrivialObjectPtr = &obj;
    return nonTrivialObjectPtr;//Address of local variable may escape the function
}

// Only creates a local copy, presumably due to the contents of copy operator
NonTrivialObject *CopyANonTrivialObject2(NonTrivialObject obj) {
    auto *nonTrivialObjectPtr = (NonTrivialObject *) malloc(sizeof(obj));
    *nonTrivialObjectPtr = obj;
    return nonTrivialObjectPtr;
}
}

int main() {
    int i = 3;
    char c = 's';
    double dub = 3.98;
    DataStorage dataStorage(4);

    NonTrivialObject nonTrivialObject(&i, &c, &dub, &dataStorage);

    NonTrivialObject* nonTrivialObject2 = CopyANonTrivialObject2(nonTrivialObject);

    cout << nonTrivialObject.int_ptr << ", " << *nonTrivialObject.int_ptr << endl;
    cout << nonTrivialObject2->int_ptr << ", " << *nonTrivialObject2->int_ptr << endl;

    free(nonTrivialObject2);

    return 0;
};

会输出

0x7ffd59c4ac28, 3
0x7ffd59c4ac28, 3

表示该副本是浅拷贝。我知道

    /*
     * Copy constructor
     */
    NonTrivialObject(const NonTrivialObject &rhs) {
        if (this != &rhs) {
            this->int_ptr = rhs.int_ptr;
            this->char_ptr = rhs.char_ptr;
            this->double_ptr = rhs.double_ptr;
            this->data_storage_ptr = rhs.data_storage_ptr;
        }
    }

是问题所在,但为了修复它,我不断遇到分段错误。我尝试了各种形式的取消引用和获取内存地址,并尝试用 memcpystd::copy 替换赋值,这两种方法在 valgrind 中都很不受欢迎。

我如何修改此 class 使其完全可复制,以便保存数据的内存位置不同但值相同?

How to properly implement a copy constructor of a class that has raw pointers?

视情况而定。

class是否拥有尖头物体?如果不涉及所有权,并且 class 仅指向其生命周期未绑定到 class 的对象,则只需复制指针即可。请注意,由于 class 在这种情况下无法控制指向对象的生命周期,因此您必须非常小心以确保指向对象的生命周期比指向的 class 实例更长它。这称为浅拷贝。

如果 class 确实拥有对象并因此对其生命周期负责,那么首先不要使用原始指针。相反,使用智能指针或容器。但是如果你要使用原始指针(但没有),那么你将动态分配指向对象的副本。这称为深拷贝。


不要在 C++ 中使用 malloc 和 free。