【问题标题】:Why can't have I a pure virtual assignment operator?为什么我不能有一个纯虚拟赋值运算符?
【发布时间】:2017-12-10 02:33:38
【问题描述】:

我对 C++ 运算符有点迷茫。我想对两个不同的类强制执行赋值运算符,即一个可以相互分配一个:

class A {
public:
    virtual A &operator =(const A &a) = 0;
};

class B : public A {
public:
    virtual A &operator =(const A &a) override {
        std::cout << "B" << std::endl;
        return *this;
    }
};

class C : public A {
public:
    virtual A &operator =(const A &a) override {
        std::cout << "C" << std::endl;
        return *this;
    }
};

int main(int argc, char *argv[])
{
    B b;
    C c;
    b = c;

    // leads to a linker error: undefined reference to `A::operator=(A const&)'
    //B b2;
    //b = b2;
}

第一个任务似乎完成了这项工作,“B”被称为。类似地,对于“c = b”,调用“C”。但是,当我取消注释第二部分时,出现链接器错误。如果我将 A 的运算符定义为:

virtual A &operator =(const A &a) {
        std::cout << "A" << std::endl;
        return *this;
} 

我得到“B”,“A”。嗯?有人可以解释为什么在分配两个 B 时需要“A”,但在 B

【问题讨论】:

  • 您确实意识到编译器还为 B 和 C 创建了默认的复制构造函数,以及默认的 NON VIRTUAL B::operator=(const B&) 和 C::operator(const C&) 运算符.... 创建从基到派生的虚拟(甚至非虚拟)赋值运算符是自找麻烦。
  • 这不是问题,但您真的需要std::endl 需要的额外内容吗? '\n' 结束一行。

标签: c++ operator-overloading assignment-operator


【解决方案1】:

编译器会生成一个隐式复制赋值运算符,当您执行 B = B 赋值时会选择该运算符。当您执行 B = C 分配时,未选择该选项。

http://en.cppreference.com/w/cpp/language/copy_assignment

https://wandbox.org/permlink/CM5tQU656rnwtrKl

如果您查看错误消息:

/tmp/cctHhd0D.o: In function `B::operator=(B const&)':
prog.cc:(.text._ZN1BaSERKS_[_ZN1BaSERKS_]+0x1f): undefined reference to `A::operator=(A const&)'
collect2: error: ld returned 1 exit status

您可以看到链接器错误来自B::operator=(B const&amp;) 内部,由于您没有定义它,这意味着它必须是自动生成的。

【讨论】:

  • 那么没有任何直接的方法可以强制程序员为给定类型提供赋值运算符吗?我猜链接器错误可以称为强制执行,但我更喜欢编译器错误。
  • @MiroKropacek 据我所知,没有。复制赋值运算符很特殊。另外,请仔细考虑从基类到派生类进行赋值是否有意义..
  • tbh,我也觉得不太舒服。这似乎只是较小的邪恶。在 B 中为“C”和在 C 中为“B”设置单独的运算符看起来更奇怪,更不用说强制执行了,将 A 中的 that 作为纯虚拟方法似乎根本不对。
【解决方案2】:

当您分配b = b2; 时,它会尝试调用 B 的默认(隐式)分配。 而默认赋值会调用基类的默认赋值,所以最终会调用A::operator=(const A &amp;a),是纯虚的。

所以你得到链接错误。

【讨论】:

    【解决方案3】:

    根据标准,派生类中基类的重写虚拟赋值运算符不会阻止生成在您的情况下被调用的默认复制赋值运算符。 B 类的默认复制赋值运算符将直接调用 A 类的复制赋值运算符,因此您会收到 undefined reference 错误。

    13.5.3 作业 [over.ass]

    2 任何赋值运算符,甚至是复制和移动赋值运算符,都可以是虚拟的。 [注意:对于具有基类 B 的派生类 D,已为其声明了虚拟复制/移动赋值,D 中的复制/移动赋值运算符不会覆盖 B 的虚拟复制/移动赋值运算符。 [示例:

    struct B {
        virtual int operator= (int);
        virtual B& operator= (const B&);
    };
    
    struct D : B {
        virtual int operator= (int);
        virtual D& operator= (const B&);
    };
    
    D dobj1;
    D dobj2;
    B* bptr = &dobj1;
    
    void f()
    {
        bptr->operator=(99); // calls D::operator=(int)
        *bptr = 99; // ditto
        bptr->operator=(dobj2); // calls D::operator=(const B&)
        *bptr = dobj2; // ditto
        dobj1 = dobj2; // calls implicitly-declared D::operator=(const D&)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-15
      • 2021-09-15
      • 2010-10-14
      • 2016-10-01
      • 2013-09-22
      • 2018-06-21
      • 2013-06-11
      • 2019-10-21
      相关资源
      最近更新 更多