【问题标题】:C++ successfully copying dynamically allocated obj without copy ctor?C ++成功复制动态分配的obj而没有复制ctor?
【发布时间】:2020-01-09 06:14:09
【问题描述】:

我有一个指向 char 的类。我想知道如果没有明确定义复制构造函数,复制构造怎么可能?

我假设是因为对象已经被分配,但如果是这样,那么为什么有人首先需要复制构造函数?

#include <iostream>
#include <cstring>

class Data {
public:
    explicit Data(const char* newData)
        :size{strlen(newData)},
         data{size ? new char[size] : nullptr}
    {
        std::memcpy(data, newData, size);
    }

    friend std::ostream& operator<<(std::ostream& ost, const Data& rhs) {
        for (std::size_t i = 0; i < rhs.size; i++) {
            std::cout << rhs.data[i];
        }
        ost << "\n";
        return ost;
    }

~Data() {
    delete[] data;
}

private:
    std::size_t size;
    char* data;
};

int main() {
    Data data1{"data1"};
    Data data2{data1}; // copy constructor
    std::cout << data1;
    std::cout << data2;

    return 0;
}

输出:

data1
data1

拷贝构造函数不应该是这样的吗?我经常看到这样的例子。但是既然默认构造函数已经这样做了,那我什么时候真正需要定义复制ctor呢?

Data(const Data& other) {
    if (this != &other) {
        size = other.size;
        data = new char[size];
        std::memcpy(data, other.data, size);
    }
}

顺便说一句,我意识到代码引入了一些 maul 实践(例如,使用 new 而不是智能 ptrs,首先不使用字符串等 - 但这只是一个练习)。

【问题讨论】:

  • 您不需要在复制构造函数中检查this != &amp;other,因为尚未构造的对象不能与给定对象相同(您不能传递不存在的对象)。不过,此检查在复制赋值运算符中很有用 :)

标签: c++ oop copy-constructor


【解决方案1】:

自 C++11 起,如果类具有用户定义的析构函数,则不推荐生成 implicitly-defined copy constructor(有充分的理由),但仍会生成它。该复制构造函数只会复制sizedata,进行浅拷贝,而不是深拷贝。这是一条通往灾难的道路,因为data 将在原始对象及其副本被销毁时被多次删除。

例如,如果您在Valgrind 下运行您的原始代码,您将看到以下报告:

==9908== HEAP SUMMARY:
==9908==     in use at exit: 0 bytes in 0 blocks
==9908==   total heap usage: 3 allocs, 4 frees, 73,733 bytes allocated
==9908== 
==9908== All heap blocks were freed -- no leaks are possible
==9908== 
==9908== For counts of detected and suppressed errors, rerun with: -v
==9908== ERROR SUMMARY: 18 errors from 9 contexts (suppressed: 0 from 0)

如果你希望Data 是可复制的,你必须提供一个深拷贝构造函数来分配新的存储空间并将数据复制到其中。您问题中的那个看起来几乎(*)很好:

==9993== HEAP SUMMARY:
==9993==     in use at exit: 0 bytes in 0 blocks
==9993==   total heap usage: 4 allocs, 4 frees, 73,738 bytes allocated
==9993== 
==9993== All heap blocks were freed -- no leaks are possible
==9993== 
==9993== For counts of detected and suppressed errors, rerun with: -v
==9993== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

如果你根本不需要复制构造,你可以通过显式删除它来禁用它的隐式生成:

Data(const Data&) = delete;

复制赋值运算符也是如此。 implicitly-defined one 不会做你想做的事。 Don't forget 关于它。

(*) 注意other.data可以是nullptr,所以你在复制之前have to check it

void* memcpy(void* dest, const void* src, std::size_t count);

如果destsrc 是空指针,则行为未定义,即使count 为零。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多