【问题标题】:Utilizing Copy constructor for =overloading为 =overloading 使用 Copy 构造函数
【发布时间】:2014-03-04 01:58:32
【问题描述】:

我有一个包含两个向量的类:intStr。现在我想定义一个复制构造函数,以便颠倒元素的顺序;例如如果a=(1,Hello),(2,World) 和我写auto b=a; 我得到b=(2,world),(1,hello)。这是完美的工作。我遇到的问题是重载 = 运算符,以便使用复制构造函数。这是我的类加上复制构造函数:

class grade
{
  private:
    static int copies;
    std::vector<int> i;
    std::vector<std::string> s;

  public:
    grade() {copies++;};
    grade (int , std::string );
    void printval();
    void adder(int , std::string );
    int getcount();

    grade(grade & obj)
    {
        std::vector<std::string>::reverse_iterator strIt = obj.s.rbegin();
        for (std::vector<int>::reverse_iterator numIt=obj.i.rbegin();
             numIt!=obj.i.rend(); ++numIt)
        {
            this->adder(*numIt, *strIt);
            strIt++;
        }

    }
    grade operator =(grade );
};

当我重载= 运算符时,会调用构造函数,但问题是没有将 valur 传递给 LHS 变量。这是重载的=

grade grade::operator=(grade cpy)
{
    grade newer = cpy; //Calls the copy constructor as expected
    return cpy;        //No error but blank value is returned to the LHS variable.
}

我的主要功能是:

int main()
{
    grade c2(1,"Hello");
    grade c1;

    c2.adder(4,"World");
    c2.printval();
    std::cout<<std::endl;
    grade c3 = c2;
    c3.printval();
    std::cout<<std::endl;
    c1 = c2;
    std::cout<<std::endl;
    c1.printval();

    return 0;
}

为什么c1 保持空白??

【问题讨论】:

  • 永远不要在 Stack Overflow 问题中说“完美运行”。

标签: c++ operator-overloading copy-constructor assignment-operator copy-and-swap


【解决方案1】:

实现赋值运算符最简单的方法是copy and swap idiom

grade& operator=( grade other )
{
   swap( *this , other );
   return *this;
}

其中 swap 是一个函数,它接受两个 grades 并交换其值:

void swap( grade& lhs , grade& rhs )
{
    using std::swap;

    swap( lhs.first_member  , rhs.first_member  );
    swap( lhs.second_member , rhs.second_member );
    swap( lhs.third_member  , rhs.third_member  );
    ...
}

注意swap()std::swap() 的重载/特化。 std::swap() 默认使用赋值运算符来执行交换,因此如果使用它会导致无限递归。你需要做的是编写你自己的交换重载来执行成员之间的交换(如上例所示)。

请注意,您应该使用不合格的swap() 来不禁用ADL,并让编译器找到成员的自定义swap() 重载(如果他们提供)或标准库版本。

【讨论】:

  • swap 会调用operator=,导致无限递归。您需要交换各个成员变量。要么为你的班级实现swap() 的重载。
  • @AdamH.Peterson 的想法是您为自己的类型重载swap
  • 啊,我看到你已经在你的答案中添加了超载。 (但您可能应该在示例 swap() 中演示成员交换。)
  • 您使用合格的std::swap 将禁用ADL,这意味着如果first_member(等)实现了自己的重载,则不会考虑它们。您应该将using std::swap; 放入函数中,然后调用swap() unqualified。
  • @AdamH.Peterson 我总是忘记 ADL 问题,谢谢
【解决方案2】:

你问:Why does c1 remain blank??

答案是赋值运算符不修改对象本身。这是你的代码:

grade grade::operator=(grade cpy)
{
  grade newer=cpy; //Calls the copy constructor as expected
  return cpy; //No error but blank value is returned to the LHS variable.
}

方法返回输入参数cpy的副本,但不修改当前实例。当这个方法执行时,*this“LHS 变量”,它根本没有被修改。在这个方法内(操作符重载,需要实际修改当前实例的成员变量!

【讨论】:

  • 我希望能回答你的问题。有关如何实际修改当前实例中的成员变量的建议,请参阅解释“复制和交换”方法的任何其他答案。 :-)
  • 该方法不返回任何内容,因为它在对 operator= 的递归调用中被锁定。
  • @Manu343726 不,这个不是。
  • @juanchopanza uups,newer,不是*this...对不起
  • 你是对的,但是如果我尝试修改 *this 我会得到一个无限循环。看起来交换是唯一的方法。
【解决方案3】:

你想要的是复制和交换习语:

#include <algorithm>
grade &grade::operator=( grade cpy ) {
    using std::swap;
    swap( i, cpy.i );
    swap( s, cpy.s );
    return *this;
}

【讨论】:

    【解决方案4】:

    如下更改复制构造函数和赋值运算符:

    grade::grade(const grade& cpy)
    {
        *this = cpy;
    }
    
    grade& grade::operator=(const grade& cpy)
    {
        if (this == &cpy)
            return *this;
    
        std::vector<std::string>::reverse_iterator strIt = cpy.s.rbegin();
        for (std::vector<int>::reverse_iterator numIt=cpy.i.rbegin();numIt!=cpy.i.rend();++numIt)
        {
            this->adder(*numIt, *strIt);
            strIt++;
        }
    
        return *this;
    }
    

    作为一般方案:

    object::object(const object& input)
    {
        *this = input;
    }
    
    object& object::operator=(const object& input)
    {
        if (this == &input)
            return *this;
    
        Deallocate();
        Allocate(input);
        Initialize(input);
    
        return *this;
    }
    

    【讨论】:

    • 在我看来(并且我相信一般的 C++ 社区),在复制方面实现分配(使用复制和交换习语)比在以下方面实现复制要好得多任务。
    • 我一直认为自我分配检查是一件愚蠢且不必要的事情。想想你上次写a = a这样的句子是什么时候......
    • @Manu343726:可能。但是如果用户这样做,并且涉及到内存分配(delete/new),那么就会出现运行时错误。
    • @Manu343726:我倾向于同意。通常,如果为了正确性而需要进行自分配检查,则代码存在异常安全问题。如果它是一种优化,请意识到自我分配通常很少见,并且自我分配检查可能不会给您带来太多收益,甚至可能会花费您。
    • 不,因为您提供的通用方案根本不是一个好的通用方案。好的方案是使用基于 RAII 的资源,而不是手动管理资源,让(在几乎所有情况下)编译器生成有效的赋值运算符、复制 ctor 和 dtor(即所谓的零规则)。在 RAII 聚合不会导致工作默认 ctors/operator= 的少数情况下,复制和交换开始发挥作用。
    猜你喜欢
    • 1970-01-01
    • 2015-05-28
    • 2015-04-17
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 1970-01-01
    • 2022-12-24
    • 2015-02-23
    相关资源
    最近更新 更多