-2

我无法理解以下代码的行为,我从 C++ 切片的示例中对其进行了修改:

#include <stdio.h>
#include <iostream>

struct B { 
    int x; 
    B() { x = 0; } 
    virtual void foo( const char* id ) { 
        std::cout << id << ": B="  << this << ", x=" << x << std::endl; 
    } 
};

struct D1 : B  { 
    int y;
    D1() { x = 1; y = 100; } 
    virtual void foo( const char* id ) { 
        std::cout << id << ": D1=" << this << ", x=" << x << ", y=" << y << std::endl; 
    } 
} d1;

struct D2 : B  {
    int z;
    D2() { x = 2; z = 200; } 
    virtual void foo( const char* id ) { 
        std::cout << id << ": D2=" << this << ", x=" << x << ", z=" << z  << std::endl; 
    } 
} d2;

void main() {
    std::cout << "d1 = " << &d1 << std::endl;
    std::cout << "d2 = " << &d2 << std::endl;

    std::cout << "By pointer at beginning: " << std::endl;
    B* pb = &d1;
    pb->foo( "pd1" );
    pb = &d2;
    pb->foo( "pd2" );

    std::cout << "By Value: " << std::endl;
    B b = d1;
    b.foo( "d1" );
    b = d2;
    b.foo( "d2" );

    std::cout << "By pointer after by value: " << std::endl;
    pb = &d1;
    pb->foo( "pd1" );
    pb = &d2;
    pb->foo( "pd2" );

    std::cout << "By reference: " << std::endl;
    B& rb = d1;
    rb.foo( "rd1" );
    rb = d2;
    rb.foo( "rd2" );

    std::cout << "By pointer after by reference: " << std::endl;
    pb = &d1;
    pb->foo( "pd1" );
    pb = &d2;
    pb->foo( "pd2" );
}

//The result is the following:
d1 = 0115B504
d2 = 0115B510
By pointer at beginning:
pd1: D1=0115B504, x=1, y=100
pd2: D2=0115B510, x=2, z=200
By Value:
d1: B=0036FE44, x=1
d2: B=0036FE44, x=2
By pointer after by value:
pd1: D1=0115B504, x=1, y=100
pd2: D2=0115B510, x=2, z=200
By reference:
rd1: D1=0115B504, x=1, y=100
rd2: D1=0115B504, x=2, y=100
By pointer after by reference:
pd1: D1=0115B504, x=2, y=100
pd2: D2=0115B510, x=2, z=200

从上面的结果我们可以看出:

  • 值分配通过删除派生的特定成员导致分配目标 (b) 的切片问题,但保留分配源 (d1 和 d2) 不变。
  • 引用分配通过不分配派生的特定成员导致对分配目标 (rd) 的切片,从而部分更改分配源 (d1 和 d2)。

起初,我很惊讶您可以通过它们的基分配对不同类型(D2 到 D1)的引用,直到我意识到臭名昭著的 C++ 强制转换系统。一个结论似乎是引用只能被初始化而不能被赋值。

我们知道基础对象的 STL 容器存在切片问题,因为除了列表,所有其他 STL 容器都在复制对象。看起来基本引用的 STL 容器应该不会更好,除非它在里面保存指针。

你们是如何处理这个问题的?

谢谢!CP

4

2 回答 2

2

我发现很难在你的文字中找到一个问题,但我会假设它是这样的:

“如何在标准容器中多态存储对象?”

首先,我只想说您不能将引用存储在标准容器中,因为您不能分配(重新绑定)它们。

所以正常的方法是有一个基类指针的容器。如果容器拥有这些项目,则使用智能指针,如unique_ptror shared_ptr。如果容器不拥有对象,则只需使用原始指针。

于 2013-06-14T18:15:05.030 回答
1
B& rb = d1;
rb.foo( "rd1" );
rb = d2;

最后一行相当于你写的

d1 = d2;

除了它仅适用于基类子对象。在第一行中,您将引用设置rbd1具有不同静态类型的别名。无法重新安装引用。后面的分配以对象为目标。d1 只能通过使用 op= 的诡计直接分配,但是您更改的静态类型使 Base::op= 工作。

许多导师说你应该只使用抽象类作为基础,并且大多数推理都指向这样的意外。

在层次结构中,您很少留下 op=,如果绝对需要,您可以使用一些虚拟符号来验证兼容性。

于 2013-06-14T18:09:05.050 回答