【问题标题】:Regarding c++ operator assign behavior when have parent child relationship关于具有父子关系时的c ++运算符分配行为
【发布时间】:2018-11-30 23:16:32
【问题描述】:

我有一个关于接受父引用类型的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&amp; operator=(const Type1&amp;) 中被重定向,而被注释忽略。

取消注释Type1&amp; operator=(const Type1&amp;) 只是为我工作, 但是如果说,我有一百多个 TypeX,那么我必须进行两百个复制分配,这是我无法理解的,因为在我看来,只有 Type1&amp; operator=(const AbstractType&amp; other) 就足够了。

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

谁能建议我更好的解决方案?

【问题讨论】:

  • 由于您无法更改现有对象的类型,因此将赋值运算符设为虚拟没有多大意义。您增加了在代码中引入错误的机会。也许您想克隆对象并动态创建它们。
  • 多态类型的赋值很少有意义。也许您需要操纵(智能)指向多态对象的指针,而不是对象本身。
  • @Phil1970, @n.m.感谢你的建议!但我有两个 AbstractionType 列表。当访问每个列表中的相同索引时,将确保相同的类型,因此它们具有相同的大小。我想做的是在循环期间进行分配。在伪代码中,它将是for i in 0 to length; *(dest[i]) = *(source[i])。这就是制作虚拟算子的原因。有什么建议吗?谢谢!
  • @0ctopus13prime 如果你做一个循环,然后显示一些代码来显示你的数组是如何声明的。我们已经告诉过您,在大多数情况下,虚拟作业没有任何意义(对于了解该语言的人),但在每个回答者中,您似乎并不真正理解任何给定的评论。 您的问题遗漏了有助于我们清楚了解您想要实现的目标的详细信息! 可能有很多方法可以解决您的问题而虚拟作业可能不是其中之一。 停止试图为工作使用错误的工具。如果你想复制一个数组,克隆就是答案。

标签: c++ operator-keyword


【解决方案1】:
// AbstractType& operator=(const AbstractType&)!! Why??
// Expected `Type1& operator=(const AbstractType&)` to be called!
t1 = t2; 

你在这里打电话:

t1.operator=(t2);

由于t1t2Type1,编译器会匹配以下函数:

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

这是隐式定义的复制赋值运算符,它将调用基的复制赋值运算符:

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

但是,此调用不是动态分派的——这就是您最终看到结果的原因。


取消注释 Type1&amp; operator=(const Type1&amp;) 只是为我工作

注意:

  • 不需要dynamic_cast
  • 无需进行虚拟通话。
  • 操作员本身不需要是virtual

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

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

【讨论】:

  • 感谢您的回答!然后最后,我无论如何都要复制两个版本第一个是Type1&amp; operator=(const AbstractType&amp;)第二个是Type1&amp; operator=(const Type1&amp;)我对吗?
  • 您可能应该只使用标准赋值运算符并更改您的设计。但是,您的问题没有足够的细节来建议您在特定情况下使用正确的方法。 一般情况下,虚拟分配不是解决方案
  • @0ctopus13prime:正如 Phil1970 所说,很难说你应该如何进行。例如,不清楚您在问题中提到的“超过一百个 TypeX”要达到什么目的。您是否尝试支持从其中任何一个分配给任何其他?或者只是到另一个相同类型的?为什么无论如何都要进行虚拟分配?等等。
【解决方案2】:

因为Liskovs substitution principle,它指出如果程序、模块或函数正在使用基类,则可以将基类的引用替换为派生类而不影响程序功能。因此,在您的特定情况下,根据Curiously recurring template pattern 实现它将是一个优雅的解决方案。请参阅link 了解更多信息!

【讨论】:

  • 感谢您的回答。我想采用这种模式,但在我看来,很难以多态方式做事。因为我在运行时只有 AbstractType 引用,所以很难知道具体的类型。如果我错了,请纠正我!谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 2018-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多