【问题标题】:Tricky inheritance of operator= in abstract class hierarchy抽象类层次结构中 operator= 的棘手继承
【发布时间】:2014-08-02 11:30:06
【问题描述】:

我有这个类层次结构,我试图在其中添加 operator= :

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

class B : public A
{
public:
    void someFunction() {
        //implementation
    }
    A& operator=(const A& o)
    {
        *ptr = *o.ptr;
        return *this;
    }

private:
    A* ptr;
};

class C : public A
{
public:
    void someFunction() {
        //implementation
    }
    A& operator=(const A& o)
    {
        data = o.data;
        return *this;
    }

private:
    int data;  //NOTE: different members that needs to be copied in the operator
};

我明白为什么这不起作用。我在 B 中有一个私有成员(需要在那里)和一个需要覆盖的函数A&operator=(const A&)。问题是 o 属于 A 类型,并且没有指针 ptr

我尝试将 dynamic_cast o 转换为类型 B,但是

  1. 这行不通,因为它是恒定的,
  2. 看起来不安全(如果 rhs 是 C 类)

C 类也有同样的问题。 是否有一些更有效的解决方法?

澄清为什么我需要这种方式:

class superClass
{
public:
  superClass& operator=(const superClass& o)
  {
    *some_A_type = *o.some_A_type;
  }
private:
  A* some_A_type;
};

本质上,我想要的是 superClass 的 operator=。我不确定在哪里或如何解决它。

【问题讨论】:

  • 为什么A中的赋值运算符放在首位?我首先要捍卫那个决定。
  • 因为后来的使用方式。我有一个 X 类,它必须能够存储任何类型的 A-child 并对它们执行操作。
  • 假设有一段时间是有道理的,当你B b; C c; b = c; 时,你期望会发生什么?存储对A 派生的引用不需要赋值运算符。它需要对真实对象的引用和/或指针,否则你最终会盯着a slicing problem的喉咙@
  • 然后你为每个类实现正确的赋值,把它放在 A 之外,并为你的多态性使用指针(最好是智能指针)或引用。
  • 也许你会找到答案here。尤其是 Brian R. Bondy 的回答看起来很有希望。

标签: c++ inheritance c++11 operator-overloading abstract


【解决方案1】:

similiar TobiMcNamobi 提出的问题中找到:

class B : public A
{
public:
  virtual A& operator=(const A& p)
  {
    *ptr = *o.ptr;
    return *this;
  }


  virtual B& operator=(const B& p)
  {
    //throw exception
  }

};

【讨论】:

  • 但是您必须为 A 的每个后代添加方法,并且在每个后代中您只有一个不会引发异常的变体。最后一切(调用哪个方法)无论如何都将在编译时决定。 IE。如果您在任何地方都使用A 工作,则只会调用operator=(const A&)
【解决方案2】:

您应该重新考虑类的初始设计。

你也应该明白:

  • 运算符多态性(a + b 适用于 std::stringint
  • 数据类型本身不能是多态的,因为应该定义内存布局
  • 什么是抽象类和/或接口
  • 也许静态多态也有用

首先尝试想象在A 类中的任何类的任何对象之间进行赋值意味着什么。 IE。要将B 的对象存储在C 的对象中,我们应该从C 更改对象的状态,这样它的特性将与B 的原始对象等效。这可以通过在A 的所有后代之间使用公共内存布局(即所有后代存储相同的数据)或以其他方式(如引用原始对象)公开相同的行为来实现。

注意virtual void someFunction() 的行为也应该被复制。

让我们尝试从您的样本中提取最大的值:

// our interface
struct A {
    virtual void someFunction() = 0;
    // no polymorphic assignment
};  

struct B : A {
    void someFunction();
    B &operator=(const A &o)
    { ptr = &o; return *this; }
private:
    A *ptr;
}   

struct C : A {
    void someFunction();
    A &operator=(const C &o)
    { data = o.data; return *this; }
private:
    int data;
};

C c, c2;
B b;
A &a = c;
b = c; // ok
b = a; // ok
c = c2; // ok
c = b; // wrong

或者如果你仍然想要多态赋值:

// interface. descendants are responsible for maintaining LSP
struct A {
    void someFunction()
    { cout << data(); }
    virtual int getData() const = 0;
    // assignment should result in copying chars and making getData() to behave like in original object
    virtual A &operator=(const A &o) = 0; 
};  

struct B : A {
    int getData() const { return ptr->getData(); }
    A &operator=(const A &o)
    { ptr = &o; return *this; }
private:
    const A *ptr;
};

struct C : A {
    int getData() const { return data; }
    A &operator=(const A &o)
    { data = o.getData(); return *this; }
private:
    int data;
};

附:最后一个变体在现实世界中可能不需要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    • 2012-06-18
    • 1970-01-01
    相关资源
    最近更新 更多