带有分配过载语法的复制构造函数

Copy Constructor with assignment overloading syntax?

本文关键字:复制 构造函数 语法 分配      更新时间:2023-10-16

我正在与编写五巨头(复制构造函数,复制分配运算符,移动构造函数,移动分配运算符,Destructor)。而且我用复制构造函数语法碰到了一些障碍。

说我有一个具有以下私人成员的类Foo:

    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对它们进行了评论,将它们汇总在下面的列表中:<<<<<<<</p>

  1. 以前的template<classname data>,不正确的功能和类必须是template <typename data>template <class data>
  2. 以前int*dataArray;这错过了模板,应为data* dataArray;

实现所需目标的最佳方法是使用已经处理分配,复制和移动的类,并为您照顾其内存管理。std::vector确实可以做到这一点,并且可以直接替换您的动态分配的数组和大小。这样做的类通常称为RAII类。


话虽如此,并且假设这是正确实施各种特殊会员功能的练习,我建议您通过副本和交换成语继续进行。(有关更多详细信息和评论,请参阅什么副本和交换成语?这个想法是根据复制构造函数来定义分配操作。

从成员,构造函数和破坏者开始。这些定义了班级成员的所有权语义:

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);)。

[ - 一边:您可以通过接受value 来避免编写第一行(明确复制对象)。这是同一回事,在某些情况下可能会更有效:

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副本不在范围内,其破坏者清除了内存。但这不是在功能开始时分配的记忆。这是我们对象使用的内存,在我们将状态与临时交换之前。

以这种方式,分配,复制和交易的细节被保留在构造函数和驱动器中的位置。分配运算符简单副本 swaps

这具有进一步的优势,而不是更简单:这是例外。在上面的代码中,分配错误可能会在创建临时性时会引发例外。但是我们尚未修改班级的状态,因此即使分配失败,我们的状态仍然保持一致(并正确)。


遵循相同的逻辑,移动操作变得微不足道。必须将移动构造函数定义为简单地将资源的所有权放置,然后将源(移动从对象)留在一个定义明确的状态下。这意味着将源的dataArray成员设置为nullptr,以便其后续的delete[]在其破坏者中不会引起问题。

移动分配运算符可以与复制分配相似,尽管在这种情况下,由于您只是在窃取已经分配的对源对象的已分配内存,因此较少关注。在完整的示例代码中,我选择简单地交换状态。

可以在此处看到一个完整的,可编译的示例。

您的foo类不会内部使用data模板参数。我想您想在这里使用它:

int * dataArray; // should be: data * dataArray;

您也不允许使用classname关键字,而是typenameclass。您还在代码中还有许多其他编译错误。

您的复制构造函数是错误的,不会编译:

foo = bar; // is this correct? - answer is NO

foo是此上下文中的类名称,因此您的假设是正确的。*this = someObject这将起作用(有了其他修复程序,至少必须将DataArray设置为nullptr),但是您的类变量将首先由复制构造函数首先构造,仅由分配运算符覆盖,因此其安静的不有效。有关更多在此处阅读的信息:

在复制构造函数中调用分配运算符

从复制构造函数调用默认分配运算符?

是不好的。