C++:复制构造函数后两个对象的值都会更改

C++: Values of both objects changes after a Copy Constructor

提问人:radar101 提问时间:6/8/2022 更新时间:6/8/2022 访问量:270

问:

我编写了一个简单的 c++ 代码来理解复制构造函数/运算符重载的概念。代码片段如下所示。

在代码中,我正在创建一个对象,然后创建一个新对象并分配 .接下来,我调用重载来更改 和 的值。vec v2v4vec v2operator[]v4[0]v4[1]

我的问题是,在分配这些值后,的值也发生了变化。 我不太确定这是怎么发生的。希望有人能帮我解决这个问题。vec v2

class vec {
private:
    // Variable to store the number of elements contained in this vec.
    size_t elements;
    // Pointer to store the address of the dynamically allocated memory.
    double* data;

public:

    vec(size_t size) : elements{ size }, data{ new double[size] } {std::cout << "First constructor" << "\n"; };
    
    vec(size_t size, double ival) : elements{ size }, data{ new double[size] } {
        std::cout << "Second constructor" << std::endl;
        for (int i = 0; i < elements; i++) {
            data[i] = ival;
        }
    }
    
    vec(std::initializer_list<double> iList): vec(static_cast<size_t>(iList.size())) {
        std::cout << "Third constructor" << std::endl;
        auto count{ 0 };

        for (auto element: iList) {
            data[count] = element;
            count++;
        }
    
    }
    
    /// Copy constructor that creates a copy of the vec variable 'v'.
    vec(const vec& v) : elements{ v.elements }, data{ new double[v.elements] }{
        std::cout << "Copy constructor " << "\n";
        std::memcpy(&data, &(v.data), v.elements);
    }
    
    /// Copy assignment operator. Creates a copy of vec variable 'v'.
    vec& operator=(const vec& v) {
        std::cout << "Copy assignment " << "\n";
        if (this != &v) {
            const auto new_data{ new double[v.elements] };
            delete[] data;

            data = new_data;
            elements = v.elements;
            std::memcpy(&data, &(v.data), v.elements);
        }
        return *this;
    }
    
    double& operator[](size_t idx){
        return this->data[idx];
    }
    
    friend std::ostream& operator<<(std::ostream& os, const vec& v) {

        for (int i = 0; i < v.elements; i++) {
            os << v.data[i] << "\n";
        }

        return os;
    }
};

int main(void) {

vec v2 = {4, 5, 6};
vec v4 = v2

v4[0] = 11;                                          // call to operator[]
v4[1] = 12;                                          // call to operator[]

return 0;
}
C++ 运算符重载 copy-constructor

评论

1赞 Sam Varshavchik 6/8/2022
专业提示:在纯 C++ 代码中使用永远没有正当理由。显示的调用并没有真正执行您认为的操作。memcpymemcpy()
0赞 PaulMcKenzie 6/8/2022
std::memcpy(&data, &(v.data), v.elements);-- 使用 ,而不是 -- 仔细阅读文档,因为最后一个参数是错误的。此外,如果这是 的向量,则使用绝对无法正常工作,即使对最后一个参数进行了“更正”。std::copystd::memcpystd::memcpystd::stringstd::memcpy
0赞 Pepijn Kramer 6/8/2022
这就是为什么你不应该有“原始指针”作为成员。说明你的意图:谁拥有什么,如果你想使用指针,请使用 std::unique_ptr 或 std::shared_ptr!或者在这种情况下,只需将成员数组设为 std::vector<double> 即可免费获得完整的默认和正确的复制行为。
0赞 PaulMcKenzie 6/8/2022
我不太确定这是怎么发生的。-- 另一个技巧是,在你甚至不知道基础知识是否正常工作之前,不要写太多代码。当基本的复制操作有问题时,为什么要涉及构造函数?该类应该有一个指针、一个“size”成员、一个填充数据的“常规”构造函数、复制构造函数、赋值运算符和析构函数。仅此而已 -- 然后在编写任何其他代码之前进行测试以确保类具有正确的复制语义。initializer_list

答:

2赞 PaulMcKenzie 6/8/2022 #1

问题是该函数的误用:std::memcpy

std::memcpy(&data, &(v.data), v.elements);

由于 和 已经是指向数据的指针,因此获取这些指针的地址会导致这些参数使用不正确的指针值。datav.data

另一个问题是,第三个参数应该表示要复制的字节数,而不是元素数。v.elements

正确的调用应该是:std::memcpy

std::memcpy(data, v.data, v.elements * sizeof(double));


但说了这么多,不要使用 ,而是使用 。这将适用于元素的数量,并且可以处理不可轻易复制的类型(例如):std::memcpystd::copystd::string

#include <algorithm>
//...
std::copy(v.data, v.data + v.elements, data);

评论

0赞 radar101 6/8/2022
嗨,PaulMcKenzie,感谢您的回复。是的,问题出在.我使用 for 循环执行了元素复制,它也有效。我尝试过并建议你,它奏效了。我可以知道是否是现代 C++ 中的推荐使用方式?std::memcpystd::memcpystd::copystd::copy
1赞 PaulMcKenzie 6/8/2022
使用 ,因为编译器将(应该)默认用于 等简单类型。因此,您不会通过显式调用获得任何好处,而只会可能导致错误(例如您遇到的错误)。用于强制执行此操作的情况,例如从一个缓冲区复制到另一个缓冲区,并且源和目标属于不同的类型。std::copymemcpydoublememcpymemcpy