我在两种智能指针之间编写了一个运算符==,并认为我应该进行快速的健全性检查。我对结果感到惊讶......
在下面的代码段中,f和b的所有变体如何以相同的值结束?
struct Foo {
int x;
};
struct Bar : public Foo {
int y;
};
#include <iostream>
int main ()
{
Bar bar;
Foo * f = &bar;
Bar * b = &bar;
std :: cout << f << " " << b << " " << (f == b) << "\n";
void * fv = f;
void * bv = b;
std :: cout << fv << " " << bv << " " << (fv == bv) << "\n";
int fi = reinterpret_cast <int> (f);
int bi = reinterpret_cast <int> (b);
std :: cout << fi << " " << bi << " " << (fi == bi) << "\n";
}
答案 0 :(得分:8)
关于基类对象与子类对象不具有相同地址的唯一时间是涉及多重继承时。
在上面的示例中,内存可能如下所示:
/ --------- \ / | x | > This is the Foo portion of bar This is the whole Bar object < --------- / \ | y | \ ---------
对象的两个视图具有相同的起点,因此指向任一视图的指针将具有相同的值。
在多重继承中,事情变得更加复杂。说你有:
struct Foo1{ int x; };
struct Foo2{ int y; };
struct Bar : public Foo1, public Foo2 { int z; };
Bar bar;
现在记忆必须像这样:
/ --------- \ / | x | > This is the Foo1 portion of bar / --------- / \ This is the whole Bar object < | y | > This is the Foo2 portion of bar \ --------- / \ | z | \ ---------
因此&bar
和(Foo1*)&bar
将具有相同的值,而(Foo2*)&bar
将具有不同的值,因为对象的Foo2
部分从更高的地址开始
答案 1 :(得分:2)
这是您最终会得到的典型内存布局:
Bar starts Foo starts x starts / ends Foo ends y starts / ends Bar ends
| | | | | | | |
Sice在Bar start和Foo start之间没有任何内容,Foo的偏移量可以为零,因此Bar,Foo和x的指针可能相同。
答案 2 :(得分:2)
一般来说,派生类的第一部分是其父类。因此,指向派生的指针和指向同一对象的指针通常是相等的。但是,这会随着多重继承而改变。在MI的情况下,一般来说,派生类的第一个pat它的父母以某种可能的任意顺序(不知道并且对此无关紧要)。因此,如果你有一个指向派生的指针,一个指向base1的指针和一个指向base2的指针,派生的乘法继承自两者,这三个指针可能不会全部相等;两个可能是。
空白*不必相同,但一般都是。
reinterpret_cast to int是相同的,因为指针的值是相同的。
答案 3 :(得分:0)
operator==()
只是评估f
和b
是否指向相同的内容,在这种情况下它们是。
答案 4 :(得分:0)
大多数编译器在子类对象的开头布局父类对象,所以地址是相同的,这里没什么惊喜。
答案 5 :(得分:0)
您基本上将f
和b
重叠到同一个内存位置(&bar
)。实际上,我对你如何期望它们与众不同感到困惑。