gpt4 book ai didi

c++ - 使用赋值重载语法复制构造函数?

转载 作者:搜寻专家 更新时间:2023-10-31 01:35:11 24 4
gpt4 key购买 nike

我正在编写五大​​类(复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符、析构函数)。我在复制构造函数语法方面遇到了一些障碍。

假设我有一个 foo 类,它具有以下私有(private)成员:

    template<class data> // edit
class foo{

private:
int size, cursor; // Size is my array size, and cursor is the index I am currently pointing at
data * dataArray; // edit
}

如果我要为此编写一个任意大小的构造函数 X它看起来像这样。

template<class data> // edit
foo<data>::foo(int X){
size = X;
dataArray = new data[size];
cursor = 0; // points to the first value
}

现在,如果我想为另一个名为 bar 的对象创建一个复制构造函数我需要做以下事情:

template<class data> // edit
foo<data>::foo(foo &bar){
foo = bar; // is this correct?
}

假设我有重载的 =来自以下代码:

 template<class data> // edit
foo<data>::operator=(foo &someObject){
if(this != someObject){
size = someObject.size;
cursor = someObject.cursor;
delete[] dataArray;
dataArray = new data[size];
for(cursor = 0; cursor<size-1;cursor++)
dataArray[cursor] = someObject.dataArray[cursor];
}

else
// does nothing because it is assigned to itself
return *this;
}

我的复制构造函数是否正确?或者应该foo = bar而是 *this = bar

我对模板化构造函数还是个新手,所以如果我在代码中犯了任何错误,请告诉我,我会更正它。

编辑 1:感谢 Marcin 在下面提供的答案,我对上面的代码进行了一些编辑,使其在语法上更加正确,并用 //edit 对其进行了评论。它们总结在下面的列表中:

  1. 以前template<classname data> ,不正确的必须是 template <typename data>template <class data>分别用于函数和类。
  2. 以前int*dataArray;这误用了模板,应该是 data* dataArray;

最佳答案

实现你想要的最好方法是使用一个已经处理分配、复制和移动的类,为你处理它的内存管理。 std::vector 正是这样做的,并且可以直接替换您动态分配的数组和大小。执行此操作的类通常称为 RAII类。


话虽如此,假设这是正确实现各种特殊成员函数的练习,我建议您通过 copy and swap idiom 继续. (有关更多详细信息和评论,请参见关于 SO 的 What is the copy and swap idiom?)。这个想法是根据复制构造函数定义赋值操作。

从成员、构造函数和析构函数开始。这些定义了您的类成员的所有权语义:

template <class data>
class foo {
public:
foo(const size_t n);
~foo();

private:
size_t size; // array size
size_t cursor; // current index
data* dataArray; // dynamically allocated array
};

template <class data>
foo<data>::foo(const size_t n)
: size(n), cursor(0), dataArray(new data[n])
{}

template <class data>
foo<data>::~foo() {
delete[] dataArray;
}

这里,内存在构造函数中分配,在析构函数中释放。接下来,编写复制构造函数。

template <class data>
foo<data>::foo(const foo<data>& other)
: size(other.size), cursor(other.cursor), dataArray(new data[other.size]) {
std::copy(other.dataArray, other.dataArray + size, dataArray);
}

(连同声明,foo(const foo& other); 在类主体内)。请注意它如何使用成员初始化列表将成员变量设置为 other 对象中的值。执行新的分配,然后在复制构造函数的主体中将数据从 other 对象复制到该对象中。

接下来是赋值运算符。您现有的实现必须执行大量指针操作,并且不是异常安全的。让我们看看如何更简单、更安全地完成此操作:

template <class data>
foo<data>& foo<data>::operator=(const foo<data>& rhs) {
foo tmp(rhs); // Invoke copy constructor to create temporary foo

// Swap our contents with the contents of the temporary foo:
using std::swap;
swap(size, tmp.size);
swap(cursor, tmp.cursor);
swap(dataArray, tmp.dataArray);

return *this;
}

(连同类内声明,foo& operator=(const foo& rhs);)。

[-- 旁白:您可以通过按值接受函数参数来避免编写第一行(显式复制对象)。这是一回事,在某些情况下可能更有效:

template <class data>
foo<data>& foo<data>::operator=(foo<data> rhs) // Note pass by value!
{
// Swap our contents with the contents of the temporary foo:
using std::swap;
swap(size, rhs.size);
swap(cursor, rhs.cursor);
swap(dataArray, rhs.dataArray);

return *this;
}

但是,如果您还定义了移动赋值运算符,那么这样做可能会导致不明确的重载。 --]

它做的第一件事是创建一个被赋值对象的拷贝。这利用了复制构造函数,因此如何复制对象的细节只需要在复制构造函数中实现一次。

复制完成后,我们将内部结构与拷贝的内部结构进行交换。在函数体的末尾,tmp 拷贝超出范围,其析构函数清理内存。但这不是在函数开始时分配的内存;在我们与临时对象交换状态之前,它是我们的对象用来保存的内存。

通过这种方式,分配、复制和释放的细节都保留在它们所属的地方,在构造函数和析构函数中。赋值运算符只是简单地复制交换

除了更简单之外,这还有一个优点:它是异常安全的。在上面的代码中,分配错误可能导致在创建临时对象时抛出异常。但是我们还没有修改类的状态,所以即使赋值失败,我们的状态也保持一致(和正确)。


遵循相同的逻辑,移动操作变得微不足道。必须定义移动构造函数以简单地获取资源的所有权并将源(移出的对象)留在定义明确的状态中。这意味着将源的 dataArray 成员设置为 nullptr,以便其析构函数中的后续 delete[] 不会导致问题。

移动赋值运算符的实现方式与复制赋值类似,但在这种情况下,不太关心异常安全性,因为您只是窃取了源对象的已分配内存。在完整的示例代码中,我选择简单地交换状态。

可以看到一个完整的、可编译和可运行的例子here .

关于c++ - 使用赋值重载语法复制构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37834156/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com