关于具有父子关系的c ++运算符分配行为

时间:2018-06-21 14:56:07

标签: c++ operator-keyword

我有一个关于operator=的问题,该问题接受父引用类型。

当有一个抽象类及其实现类时, 为什么仅凭一个operator=就可以接受父级引用类型呢?

下面是我的代码

#include <iostream>

class AbstractType {
    public:
        virtual ~AbstractType() {}
        virtual AbstractType& operator=(const AbstractType& other) {
            std::cout << "AbstractType& operator=(const AbstractType&)" << std::endl;
            return *this;
        }
};

class Type1: public AbstractType {
    public:
        virtual ~Type1() {}
        virtual Type1& operator=(const AbstractType& other) {
            std::cout << "Type1& operator=(const AbstractType&)" << 
std::endl;
            return *this;
        }

        /*
        virtual Type1& operator=(const Type1& other) {
            std::cout << "Type1& operator=(const Type1&)" << std::endl;
            // Just redirecting here! What a waste!
            return operator=(dynamic_cast<const AbstractType&>(other));
        }
        */
};

int main()
{
    Type1 t1;
    AbstractType& r1 = t1;

    Type1 t2;
    AbstractType& r2 = t2;


    // Type1& operator=(const AbstractType&). It's fine!
    r1 = r2;

    // AbstractType& operator=(const AbstractType&)!! Why??
    // Expected `Type1& operator=(const AbstractType&)` to be called!
    t1 = t2; 

    return 0;
}

您可以发现Type1& operator=(const Type1&)中只是重定向给定的参数,而该注释被注释忽略了。

取消注释Type1& operator=(const Type1&)仅对我有用, 但是如果说,我有一百多个TypeX,那么我必须进行200个拷贝分配,这是我无法理解的,因为在我看来,仅拥有Type1& operator=(const AbstractType& other)就足够了。

大多数情况下,我只有AbstractType来处理周围的事情。 很少会在有限的情况下提前知道它的特定类型。

有人可以建议我更好的解决方法吗?

2 个答案:

答案 0 :(得分:2)

// AbstractType& operator=(const AbstractType&)!! Why??
// Expected `Type1& operator=(const AbstractType&)` to be called!
t1 = t2; 

您在这里打电话:

t1.operator=(t2);

由于t1t2具有Type1,因此编译器将匹配以下函数:

Type1 & Type1::operator=(const Type1 &);

是隐式定义的副本分配运算符,它将调用基础的副本分配运算符:

AbstractType & AbstractType::operator=(const AbstractType &);

但是,此调用不会动态调度-这就是您最终看到结果的原因。


  

取消注释Type1& operator=(const Type1&)仅对我有用

请注意:

  • 不需要dynamic_cast
  • 无需进行虚拟呼叫。
  • 运算符本身不必是virtual

换句话说,您可以简化为:

Type1& operator=(const Type1& other)
{
    return Type1::operator=(static_cast<const AbstractType&>(other));
}

答案 1 :(得分:0)

由于Liskovs substitution principle,它指出如果程序,模块或函数正在使用基类,则可以用派生类替换基类的引用,而不会影响程序的功能。因此,在您的特定情况下,以Curiously recurring template pattern的方式实施将是一个很好的解决方案。有关更多信息,请参见link