我正在阅读一些有效的c ++,我意识到在我的思考过程中我可能不正确。
class A
{
public:
void laka()
{
const void * raw = dynamic_cast<const void*>(this);
cout << raw << endl;
}
virtual ~A() = 0;
};
A::~A() {}
class B : public A
{
public:
void ditka() {}
};
int _tmain(int argc, _TCHAR* argv[])
{
B b;
cout << &b << endl;
b.laka();
return 0;
}
这本书说通过使用带有* void的dynamic_cast,我会得到一个对象的起始地址,但是所有的地址输出都是相同的。
当我输出上面的普通旧&amp; b的地址时,地址是否显示了派生对象的起始地址或b中的基础对象?
如果我对#1不正确或错误,我如何获得b中每个子对象的起始地址?我是否只需要手动偏移,dynamic_cast如何使用它或者只是澄清作者的意思?
答案 0 :(得分:5)
大多数继承实现将第一个基类子对象放在派生类的开头,所以你真的需要两个基类,都有数据成员才能看到这个。考虑:
#include <iostream>
struct B1 {
int x;
virtual ~B1() { }
};
struct B2 {
int y;
virtual ~B2() { }
};
struct D : B1, B2 { };
int main() {
D x;
B1* b1_ptr = &x;
B2* b2_ptr = &x;
std::cout << "original address: " << &x << "\n";
std::cout << "b1_ptr: " << b1_ptr << "\n";
std::cout << "dynamic_cast b1_ptr: " << dynamic_cast<void*>(b1_ptr) << "\n";
std::cout << "b2_ptr: " << b2_ptr << "\n";
std::cout << "dynamic_cast b2_ptr: " << dynamic_cast<void*>(b2_ptr) << "\n";
}
示例输出(来自我的机器;您的结果将类似):
original address: 0030FB88
b1_ptr: 0030FB88
dynamic_cast b1_ptr: 0030FB88
b2_ptr: 0030FB90
dynamic_cast b2_ptr: 0030FB88
这告诉我们B1
的{{1}}子对象位于开头,因此它与作为子对象的D
对象具有相同的地址。
D
子对象位于不同的地址,但是当您在指向B2
子对象的指针上使用dynamic_cast<void*>
时,它会为您提供B2
的地址它是一个子对象的对象。
答案 1 :(得分:0)
这是所有依赖编译器和实现的。在你的情况下,B是A +的东西,所以它是A,然后是B特定的成员。因此&amp; b的地址和dynamic_cast显示的地址应该是相同的。
答案 2 :(得分:0)
这本书是正确的,dynamic_cast
到 cv-qualified void*
将指针转换为指向您提供的指针所指向的最派生对象的指针,所以你得到派生对象的起始地址。您的输出语句都应打印相同的地址(假设std::ostream
没有特定的B*
和operator<<
重载),因为b
是派生最多的对象。
没有理由基类子对象不能具有与派生对象相同的起始地址,这是许多实现中经常发生的情况,至少对于派生类中的第一个基类子对象。
答案 3 :(得分:0)
- 当我输出上面普通旧&amp; b的地址时,是地址 显示了起始地址 派生对象或基础对象 在b?
醇>
你可以说“是”,它是A
中基础对象类b
的起始地址(与派生对象b
本身的起始地址相同) ...但派生对象实际上并不是与基础对象“分离”的对象。派生对象也不一定以从基础对象的固定偏移量开始,特别是如果它是具有虚函数的非POD类(普通旧数据类型),因为基础和派生的第一个地址objects是指向特定于基础或派生对象的v表的指针。所以你不能真正将派生对象“切片”成“基础对象”和派生对象,除了对于大多数编译器实例而言,派生对象非静态数据成员将在非偏移之后出现。基础对象的静态数据成员。但同样,任意“切片”将导致v表指针出现问题,并且对于非POD类,任何私有非静态成员对象都可以以“优化”方式分配,这可能使得基础之间的内存布局和派生的对象并不是一个干净的“切片”。