问题 基类引用 - 为其指定其他类型


以下示例中会发生什么?

struct B { };
struct D1 : B  { };
struct D2 : B  { };
int main()
{
    D1 d;
    D2 d2;
    B& x = d;
    x = d2;
}

我知道引用没有重新分配。 x 还是指 d,但是你怎么能分配 d2 至 d

多一点:

struct B
{
    B () { x = 0; }
    int x;
    virtual void foo () { cout << "B" << endl; }
};
struct D1 : B
{
    D1 () { x = 1; }
    virtual void foo () { cout << "D1" << endl; }
};
struct D2 : B
{
    D2 () { x = 2; }
    virtual void foo () { cout << "D2" << endl; }
};

int main()
{
D1 d;
D2 d2;
B& x = d;
x.foo();   //D1
               //x.x is 1 here
x = d2;
x.foo();   //also D1
               //but x.x is 2 here
}

这好像是 x.x 更新了,但vftable不是......为什么?


10156
2018-06-16 17:38


起源

+1。好问题。 - Nawaz


答案:


x 是指 B 基类 子对象 的 d。分配 x = d2   该 B 基础子对象来自 d2 并将其值分配给子对象 d

这通常不是故意做的。

编辑:

似乎x.x已更新,但vftable不是......为什么?

这就是赋值运算符 B::operator= 确实。 C ++中的基类完全没有意识到它们是基类。此外,在其生命周期内不能改变对象的类型。最接近的选择是C ++ 11 std::move,这可以转移旧 B 里面的物体 D1 变成新鲜的 D2 目的。然后,您将销毁旧对象。


11
2018-06-16 17:44



嗯,可以复制作业 virtual, 顺便一提? (懒得试试/查找) - Konrad Rudolph
我以为使用引用可以防止切片......不是吗? - AMCoder
老实说,我不知道这一点。其实我以前没想过这个。 +1。 - Nawaz
@AMCoder使用引用可以在创建对象时立即切片,但仍然可以。 - Potatoswatter
@KonradRudolph您需要双重调度才能使其工作,因为基类不理解没有RTTI的派生参数。 - Potatoswatter


如果需要,可以自己实现=并通过检查适当的具体类型(或给出错误)来“避免”切片。请参阅以下带错误的示例。

struct B { 
  virtual B& operator = (B& b) = 0;
};
struct D1 : B  { 
  D1& operator = (B& b) {
    if ( dynamic_cast<D1*>(&b) == 0 ) {
      cerr << "Cannot assign non D1 to D1" << endl;
      exit(255);
    }
    // handle the assignments
    return *this;
  }
};
struct D2 : B  { 
  int c;
  D2& operator = (B& b) {
    if ( dynamic_cast<D2*>(&b) == 0 ) {
      cerr << "Cannot assign non D2 to D2" << endl;
      exit(255);
    }
    // handle the assignments
    return *this;
  }
};

3
2018-06-16 18:01



我想他是在问这个行为,而不是如何解决这个问题...... - Luchian Grigore
这不是我要问的...... - AMCoder
+1,好主意,但是 throw 会比。更好 exit。 - Potatoswatter
RTTI让小耶稣哭了起来。仍然是+1 - TeaOverflow
哦......请注意整个实施可能 D &operator=(B const & b) { return * this = dynamic_cast< D const & >( b ); },假设不需要特殊的语义。那就扔了 std::bad_cast 在类型检查失败。 - Potatoswatter


在您的情况下,当您分配这种方式时,不会属于Base类的成员将被切片。这意味着,在这种情况下,它被复制,就像你将一个Base类对象分配给另一个。


1
2018-06-16 17:44



你在谈论对象切片(因为这就是所谓的)吗? - AMCoder
是的,我忘记了这个词。 - Spo1ler