【问题标题】:Assuming I have assignment operator, is my copy constructor being this = obj okay?假设我有赋值运算符,我的复制构造函数是 this = obj 好吗?
【发布时间】:2017-09-17 20:06:31
【问题描述】:

基本上,我的问题如下:假设我为一个类创建了一个赋值运算符,是否违反约定或不赞成让我的复制构造函数只是 this = item?

假设我正在创建一个仅包含以下数据的模板类:

private:
    int       _size;
    ItemType* _array;

如果我的赋值运算符如下:

template<class ItemType>
void obj<ItemType>::operator = (const obj & copyThis){
    _size = copyThis.getSize(); 
    _array = new ItemType[_size];
    for (int i = 0; i < _size; i++){
        //assuming getItemAt is a function that returns whatever is in given location of object's _array
        _array[i] = copyThis.getItemAt(i);
    }
}

如果我的复制构造函数简单如下,会不会违反约定/被看不起/被认为不正确?

template<class ItemType>
obj<ItemType>::obj(const obj & copyThis){
    this = copyThis;
}

【问题讨论】:

  • 你试过编译吗?
  • 你的赋值操作符会泄漏内存并且不处理自赋值。
  • 你的复制构造函数坏了。是内存泄漏。因此,您最终会得到一个基本正常的构造函数,以及一个像筛子一样泄漏内存的损坏的复制构造函数。
  • operator= 将数据从现有对象复制到另一个现有对象,因此您必须在替换现有旧数组之前delete[]。你没有这样做,所以你正在泄漏旧数组。
  • @ethanbin 你对this的理解不正确。请参阅 C++ 标准的第 9.3.2 节,“class X 的成员函数中this 的类型是X*。”

标签: c++ copy-constructor assignment-operator


【解决方案1】:

在复制构造函数中调用operator= 通常是安全的(只要operator= 不尝试将复制构造函数用作其逻辑的一部分)。

但是,您的 operator= 一开始就实施错误。它会泄漏内存,不处理分配给自身的this,并且不返回对this 的引用。

试试这个:

template<class ItemType>
obj<ItemType>::obj(const obj & copyThis)
    : _size(0), _array(0)
{
    *this = copyThis;
}

template<class ItemType>
obj<ItemType>& obj<ItemType>::operator=(const obj<ItemType> &copyThis)
{
    if (this != &copyThis)
    {
        int newSize = copyThis.getSize(); 
        ItemType *newArray = new ItemType[newSize];

        // consider using std::copy() instead:
        //
        // std::copy(copyThis._array, copyThis._array + newSize, newArray);
        //
        for (int i = 0; i < newSize; ++i) {
            newArray[i] = copyThis.getItemAt(i);
        }

        delete[] _array;
        _array = newArray;
        _size = newSize; 
    }

    return *this;
}

话虽如此,通常最好使用复制构造函数实现operator=,而不是相反:

template<class ItemType>
obj<ItemType>::obj(const obj & copyThis)
    : _size(copyThis.getSize()), _array(new ItemType[_size])
{
    for (int i = 0; i < _size; ++i){
        _array[i] = copyThis.getItemAt(i);
    }

    // or:
    // std::copy(copyThis._array, copyThis._array + _size, _array);
}

template<class ItemType>
obj<ItemType>& obj<ItemType>::operator=(const obj<ItemType> &copyThis)
{
    if (this != &copyThis)
    {
        obj<ItemType> tmp(copyThis); 
        std::swap(_array, tmp._array);
        std::swap(_size, tmp._size);
    }

    return *this;
}

如果添加swap方法可以稍微清理一下:

template<class ItemType>
obj<ItemType>::obj(const obj & copyThis)
    : _size(copyThis.getSize()), _array(new ItemType[_size])
{
    for (int i = 0; i < _size; ++i){
        _array[i] = copyThis.getItemAt(i);
    }
}

template<class ItemType>
void obj<ItemType>::swap(obj<ItemType> &swapThis)
{
    std::swap(_array, swapThis._array);
    std::swap(_size, swapThis._size);
}

template<class ItemType>
obj<ItemType>& obj<ItemType>::operator=(const obj<ItemType> &copyThis)
{
    if (this != &copyThis) {
        obj<ItemType>(copyThis).swap(*this); 
    }

    return *this;
}

话虽如此,如果您将手动数组替换为std::vector,那么您根本不需要手动实现复制构造函数或复制赋值运算符,编译器生成的默认值就足够了(因为@ 987654333@ 已经实现了复制语义)。

【讨论】:

  • 啊,这解释了我犯的错误,其他人评论的非常清楚,谢谢!但是,除了在复制构造函数中调用 operator= 是安全的之外,它是否违反约定或被视为不寻常?
  • 如果我错了,请纠正我,但是通常不是将operator=实现为按值获取参数,因此您可以使用the copy-and-swap idiom,使operator=nothrow,并且通常对您的代码进行重复数据删除/简化?
  • @ShadowRanger:这是一种常见的实现方式,尽管它确实具有始终制作副本的副作用,即使对于自分配也是如此。正如您在我的回答中看到的那样,即使参数通过 const 引用传递,复制和交换仍然是一种选择,但至少自赋值可以避免复制。
  • @RemyLebeau:是的。我通常认为值得避免增加常见(非自赋值情况)的开销,特别是有效地使用右值(因为它可以完全避免复制操作)。
  • @ShadowRanger:在 C++11 及更高版本中使用右值引用时,通常按值传递参数更有意义,并让编译器决定是使用复制还是移动语义(前提是您正确实现了两者)。对于早期的 C++ 版本,改为通过 const 引用传递更有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-04
  • 2020-12-03
相关资源
最近更新 更多