关于基础和派生对象的动态转换和地址

时间:2011-05-14 23:17:20

标签: c++

我正在阅读一些有效的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,我会得到一个对象的起始地址,但是所有的地址输出都是相同的。

  1. 当我输出上面的普通旧&amp; b的地址时,地址是否显示了派生对象的起始地址或b中的基础对象?

  2. 如果我对#1不正确或错误,我如何获得b中每个子对象的起始地址?我是否只需要手动偏移,dynamic_cast如何使用它或者只是澄清作者的意思?

4 个答案:

答案 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)

  
      
  1. 当我输出上面普通旧&amp; b的地址时,是地址   显示了起始地址   派生对象或基础对象   在b?
  2.   

你可以说“是”,它是A中基础对象类b的起始地址(与派生对象b本身的起始地址相同) ...但派生对象实际上并不是与基础对象“分离”的对象。派生对象也不一定以从基础对象的固定偏移量开始,特别是如果它是具有虚函数的非POD类(普通旧数据类型),因为基础和派生的第一个地址objects是指向特定于基础或派生对象的v表的指针。所以你不能真正将派生对象“切片”成“基础对象”和派生对象,除了对于大多数编译器实例而言,派生对象非静态数据成员将在非偏移之后出现。基础对象的静态数据成员。但同样,任意“切片”将导致v表指针出现问题,并且对于非POD类,任何私有非静态成员对象都可以以“优化”方式分配,这可能使得基础之间的内存布局和派生的对象并不是一个干净的“切片”。