移动子类中的赋值运算符未被调用/继承

时间:2015-11-10 00:35:10

标签: c++ c++11

我有一个简单的基类和两个继承它的类。问题是没有使用每个子类中的移动赋值运算符。这基本上就是我所拥有的:

class Parent {
public:
    virtual ~Parent();
    virtual Parent& operator=(Parent &&parent);
};

class Child1 : public Parent {
public:
    ~Child1();
    Child1& operator=(Child1 &&child);
};

class Child2 : public Parent {
public:
    ~Child2();
    Child2& operator=(Child2 &&child);
};

这些实现充满了日志,因此我知道被调用的内容以及何时调用。然后,我运行这个(伪)代码:

Parent &p {};
if (x == 1)
    p = Child1 {};
else
    p = Child2 {};

我得到的输出看起来像这样:

: Child constructor
: Parent move operator
: Child destruct
: SIGSEGV

知道我做错了吗?

3 个答案:

答案 0 :(得分:3)

这是slicing的经典案例,您将派生类型的实例分配给基本类型的实例。要解决这个问题,你必须使用动态分配和指针:

std::unique_ptr<Parent> p;

if(x == 1)
  p = std::make_unique<Child1>();
else
  p = std::make_unique<Child2>();

答案 1 :(得分:2)

C ++不会这样做。您不能仅仅将子类对象分配给基类对象并希望它能够工作;这个错误被称为&#34;对象切片&#34;。

此外,您的if语句的条件不是比较,而是它的分配。

答案 2 :(得分:1)

您在以下情况下重载operator= :(伪代码)

Parent = Parent&&
Child1 = Child1&&
Child2 = Child2&&

然而,您的代码会尝试执行Parent = Child1,这不是其中一个选项。请注意,左侧是定义函数的类类型 - 返回值类型不会影响函数重载。

在C ++中永远不会从基类到派生类的隐式转换,因为这太危险了。 (您必须使用强制转换来请求该行为)。例如,在这种情况下,将Parent作为参数传递给期望Child1的函数是错误的,因为父级不是Child1。

但是,存在从派生类到基类引用的隐式转换。因此Parent = Child1将匹配Parent = Parent&&。它不能与其他任何一个匹配,因为左侧没有隐式转换为派生类。

要解决此问题,您的选择包括:

  • Parent = Child1&&内明确定义Parent = Child2&&Parent(这需要转发声明)
  • Parent = Parent&&使用tag-dispatching或dynamic_cast或其他方式,以实现所有子类的所需行为

正如其他人所提到的,也许你的设计需要重新思考,因为你正在故意切片,但很少有切片是面向对象设计的预期部分。您可能希望Parent p;实际上是父级的引用或指针,而不是实际的父级。