C++ 赋值运算符=派生类重载

jan*_*nde 3 c++ list operator-overloading derived-class assignment-operator

我目前正在编写一个复杂的类,在其中我基本上需要复制派生类的列表。简化版本如下:我有一个基类,从中派生出几个其他类:

class Base
{
public:
    virtual void test(void)
    {
        cout << "Base" << endl;
    }
    Base(vector<Base*> *pointer)
    {
        pointer->push_back(this);
    }
    virtual Base& operator=(const Base& rhs)
    {
        cout << "Base=" << endl;
        return *this;
    }
};
class A : public Base
{
public:
    void test(void)
    {
        cout << "A" << endl;
    }
    A(vector<Base*> *pointer) : Base(pointer) {}
    A& operator=(const A& rhs)
    {
        cout << "A=" << endl;
        return *this;
    }
};
class B : public Base
{
public:
    void test(void)
    {
        cout << "B" << endl;
    }
    B(vector<Base*> *pointer) : Base(pointer) {}
    B& operator=(const B& rhs)
    {
        cout << "B=" << endl;
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

然后我创建一个对象列表,将其保存在基类的指针列表中:

vector<Base*> listA;

new Base(&listA);
new A(&listA);
new B(&listA);
Run Code Online (Sandbox Code Playgroud)

然后,我想将这些对象复制到具有相同类(相同顺序)但可能具有不同值的第二个列表中。

for (int i = 0; i < (int)listA.size(); i++)
{
    (*listA[i]) = (*listB[i]);
}
Run Code Online (Sandbox Code Playgroud)

然而c++无法做到这一点。由于该列表的类型为 Base*,因此解除引用会创建一个 Base 类型的对象。因此,调用基类的赋值运算符=,而不是派生类中正确的赋值运算符=。我怎样才能解决这个问题?

或者我如何告诉 c++ 使用正确的运算符?也许通过一些 isinstanceof-function ?

完整示例请参见:

int main()
{
    vector<Base*> listA;

    new Base(&listA);
    new A(&listA);
    new B(&listA);

    vector<Base*> listB;

    new Base(&listB);
    new A(&listB);
    new B(&listB);


    for (int i = 0; i < (int)listA.size(); i++)
    {
        (*listA[i]).test();
    }
    for (int i = 0; i < (int)listA.size(); i++)
    {
        (*listA[i]) = (*listB[i]);
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个输出:

Base
A
B
Base=
Base=
Base=
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

这里有一些误解。首先,将派生类的实例分配给基类的实例意味着什么?让我们看一个简单的层次结构:

struct A { int x; };
struct B : A { int y; };

A a;
B b;
a = b; // what should this do?
b = a; // what about this?
Run Code Online (Sandbox Code Playgroud)

对于普通的 C++,第一个执行对象切片,第二个是格式错误的。但即使是第一个,格式良好,通常也不是您想要做的。你确定要切片吗?


第二个是当您将赋值运算符设为虚拟时:

virtual Base& operator=(const Base& rhs)
Run Code Online (Sandbox Code Playgroud)

没有任何派生类实际重写它。A的赋值运算符接受一个A const&,并且B的 接受一个B const&。如果您用 标记两者override,您的编译器会向您指出这一点。如果你修复这两个问题来进行Base const&争论,那么你会得到你想要打印的内容 - 但它可能仍然不是你真正想要发生的事情。


为了真正制作多态副本,典型的解决方案是提供虚拟克隆方法:

virtual Base* clone() const = 0;
Run Code Online (Sandbox Code Playgroud)

您的派生类实现:

struct A : Base {
    A* clone() const override { return new A(*this); }
};
Run Code Online (Sandbox Code Playgroud)

然后使用clone()而不是赋值。这里不会有切片。


在此插入有关内存管理和原始指针的常见警告。