在 C++ 中复制构造函数?

Posted

技术标签:

【中文标题】在 C++ 中复制构造函数?【英文标题】:Copy constructor in c++? 【发布时间】:2016-02-18 06:41:57 【问题描述】:

所以我必须在 C++ 中编写一个 operator= 方法,将一个数组的所有值复制到另一个数组中。这是我写的:

dynamic_array &dynamic_array::operator=(const dynamic_array &a) 
    size = a.get_size();
    if (size % BLOCK_SIZE == 0) //a multiple of BLOCK_SIZE
        allocated_size = size;
     else 
        int full_blocks = size / BLOCK_SIZE;
        allocated_size  = (full_blocks+1) * BLOCK_SIZE;
    
    try 
        array = new int[allocated_size];
     catch (bad_alloc)
        throw exception (MEMORY_EXCEPTION);
    

    //copy a[i..size-1]
    for (int i = 0; i < size; i++)
        array[i] = a[i];
    
    return *this; //returns a reference to the object

所以它不对大小做任何假设,而是设置给定数组的大小和分配的大小(并使用另一个 get_size() 方法)。现在我必须编写的第二个代码只是说我必须创建一个包含 a 中元素副本的数组。现在我只写了和我的 operator= 方法一样的东西(没有返回任何东西):

dynamic_array::dynamic_array(dynamic_array &a) 
    size = a.get_size();
    if (size % BLOCK_SIZE == 0) //a multiple of BLOCK_SIZE
        allocated_size = size;
     else 
        int full_blocks = size / BLOCK_SIZE;
        allocated_size  = (full_blocks+1) * BLOCK_SIZE;
    
    try 
        array = new int[allocated_size];
     catch (bad_alloc)
        throw exception (MEMORY_EXCEPTION);
    

    //copy a[i..size-1]
    for (int i = 0; i < size; i++)
        array[i] = a[i];
    

现在这给了我想要的输出,但我只是想知道是否有更简单的方法来执行这两种方法。这些方法适用于动态数组,我觉得代码行数比需要的多。 operator= one 应该将 a 的元素复制到一个新的动态数组中,dynamic_array::dynamic_array(dynamic_array &amp;a) 应该创建一个包含 a 中元素副本的新数组。听起来每种方法的代码相同,因为您总是需要创建一个新数组,并且总是需要将数组元素从一个数组复制到另一个数组,但是有没有更简单的方法来编写这两种方法,或者这是最简单的怎么做?

【问题讨论】:

查找“复制和交换成语”***.com/q/3279543/214671 bad_alloc只扔exception是什么原因? @MatteoItalia 这是复制和交换在性能方面相当欠佳的情况 @M.M 效率低下,但问题中的代码相当于手工复制和交换!只有在决定分配之前查看现有数组和分配的大小,我才会认为它正在获得优势。 是的,如果这样做会更好 【参考方案1】:

您可以通过将array 的类型更改为std::vector&lt;int&gt; 来简化复制构造函数和复制赋值运算符。

向量为您完成所有工作,您甚至根本不需要任何自定义实现。

【讨论】:

好建议,虽然这可能是尝试实现类似矢量的东西。【参考方案2】:

首先,为了简化这一点,您可以利用您可以访问a 的所有私有成员这一事实。此外,使用memcpy 将使其性能更好。还添加了几行以在复制之前删除现有数组。

dynamic_array &dynamic_array::operator=(const dynamic_array &a) 
    size = a.size;
    allocated_size = a.allocated_size;

    int* newArray = new int[allocated_size];

    //copy array
    memcpy(newArray, a.array, allocated_size * sizeof(int));

    // Delete existing array
    if (array != NULL)
        delete[] array;

    array = newArray;

    return *this; //returns a reference to the object

其次,对于拷贝构造函数,我们习惯使用的一个技巧是在拷贝构造函数内部调用相等运算符,以免重复代码:

dynamic_array::dynamic_array(dynamic_array &a) : array(NULL) 
    *this = a;

【讨论】:

也添加到答案中 我不会担心为了摆脱if而为罕见的自分配情况浪费一点CPU时间。 如果新的allocated_size 相等或更小,您可以不执行deletenew 来优化这一点 在复制构造函数中使用= 是个坏主意。这意味着您必须默认构建可能会浪费大量时间的所有内容。如果你想避免重复,那么它应该是调用复制构造函数的赋值运算符,而不是相反。 (参见“复制和交换”) 建议使用std::copy而不是memcpy;如果类型是非 POD,它会表现得更好【参考方案3】:

来自 Scot Meyers(经典)书籍《Effective C++》:

“实际上,这两个复制函数通常具有相似的主体,这可能会诱使您尝试通过让一个函数调用另一个函数来避免代码重复。您避免代码重复的愿望值得称赞,但拥有一个复制函数调用另一个是错误的实现方式。

让复制赋值运算符调用复制构造函数是没有意义的,因为你会尝试构造一个已经存在的对象。这太荒谬了,甚至没有语法。有些语法看起来像你在做,但你不是;并且有一些语法以一种向后的方式执行此操作,但它们在某些情况下会破坏您的对象。所以我不会向你展示任何这些语法。只需接受让复制赋值运算符调用复制构造函数是您不想做的事情。

反过来尝试——让复制构造函数调用复制赋值运算符——同样是荒谬的。构造函数初始化新对象,但赋值运算符仅适用于已经初始化的对象。对正在构建的对象执行分配意味着对尚未初始化的对象执行某些操作,这仅对已初始化的对象有意义。废话!不要尝试。

相反,如果您发现您的复制构造函数和复制赋值运算符具有相似的代码主体,请通过创建第三个成员函数来消除重复,这两者都调用。这样的函数通常是私有的,通常被命名为 init。这种策略是一种安全且经过验证的方法,可以消除复制构造函数和复制赋值运算符中的代码重复。”

顺便说一句,在你的赋值运算符中检查自相等是明智的。

【讨论】:

Effective C++ 最初发表于 14 年前。如今,根据复制构造函数(即通过值传递源对象,即T &amp;T::operator=(T))来实现复制赋值运算符是很常见的。 @FrerichRaabe 您能否将我重定向到某个在线示例或某本书?谢谢。 好吧,我说得不准确:在T &amp;T::operator=(T) 中,您确实使用了一个副本——但是operator= 没有调用复制构造函数,而是由调用者 代码调用。所以从技术上讲,赋值运算符不是在复制构造方面实现的,但这个想法仍然适用于你的论点(即减少代码重复)。 检查复制和交换习语。它使用来自 operator= 的复制构造函数的代码。 ***.com/q/3279543/1771055

以上是关于在 C++ 中复制构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

C++——构造函数析构函数以及复制构造函数

如何在 Mac OS 上使用 Eclipse 生成 C++ 构造函数/复制构造函数

C++ 中类的构造函数理解

在 C++ 中禁止复制构造函数的最可靠方法是啥?

c++的复制构造函数

在 C++ 中复制构造函数?