4

如果我运行以下代码,我会打印出不同的地址。为什么?

class Base1 {
    int x;
};

class Base2 {
    int y;
};

class Derived : public Base1, public Base2 {

};

union U {
    Base2* b;
    Derived* d;
    U(Base2* b2) : b(b) {}
};

int main()
{
    Derived* d = new Derived;

    cout << d << "\n";
    cout << U(d).d << "\n";

    return 0;
}

更有趣的是,如果你反复进出联合,地址会不断增加 4,就像这样

int main()
{
    Derived* d = new Derived;

    cout << d << "\n";
    d = U(d).d;
    cout << d << "\n";
    d = U(d).d;
    cout << d << "\n";

    return 0;
}

如果像这样修改联合,那么问题就消失了

union U {
    void* v;
    Base2* b;
    Derived* d;
    U(void* v) : v(v) {}
};

此外,如果任一基类为空,问题就会消失。这是编译器错误吗?我希望它不要管我的指针。

4

2 回答 2

3

如果我运行以下代码,我会打印出不同的地址。为什么?

因为对象的Base2Derived对象不在对象的开头Derived。所以地址不一样。当编译器执行从 aDerived*到 a的隐式转换时Base2*,它需要调整地址。

鉴于 和 类的定义,Base1Base2的两个子对象Derived不可能位于Derived对象的起始地址——在该地址处没有空间容纳两个子对象。

假设您有以下代码:

Derived* d = new Derived;

Base1* pb1 = d;
Base2* pb2 = d;

pb1pb2指向同一个地址怎么可能?pb1必须指向一个Base1::x项目,并且pb2必须指向一个Base2::y项目(并且这些项目必须是不同的)。

更有趣的是,如果你反复进出联合,地址会不断增加 4

因为您d在编写成员后从工会成员中读取b,这是未定义的行为(您实际上是在执行类似 a reinterpret_cast<Derived*>()on a 的操作Base2*)。

我希望它不要管我的指针。

如果你想要一个Base2*指针,则不是。多重继承使事情变得更加复杂——这就是为什么许多人建议避免它,除非绝对必要。

于 2012-10-20T04:52:21.487 回答
2

联合构造函数从不初始化成员 d

联合构造函数有一个错误,它不是使用参数 b2 初始化成员 b,而是使用自身初始化 b

// b(b) should probably be b(b2)
U(Base2* b2) : b(b) {}

当您的第一个主函数示例尝试构造 U 的实例并打印成员 d 时,它实际上是在打印未定义的值,因为成员 d 尚未初始化,并且不能保证可访问。

// U(d) doesn't construct member d, so .d returns an undefined value
cout << U(d).d << "\n";

关于您的第二个主要功能示例

// d is set to a newly constructed instance of Derived
Derived* d = new Derived;

// current address of d is printed
cout << d << "\n";

// a new instance of U is constructed. The address of member d will be in close
// proximity to the newly initialized U instance, and is what will be printed
d = U(d).d;
cout << d << "\n";

// yet another new instance of U is constructed, and again, the address of member
// d will be in close proximity to the newly initialized U instance, and is
//what will be printed
d = U(d).d;
cout << d << "\n";
于 2012-10-20T04:53:40.197 回答