0

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

#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;
        }
    }

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

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

4

1 回答 1

2

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

这取决于。

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

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


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

于 2020-05-15T12:21:28.493 回答