C++ 内存布局 - Part3: 隐式转换为基类指针

0. 结论

隐式类型的指针向上转换,也就是派生类指针转换为基类指针,效果和通过static_cast做向上转换是一样的,都会将指针偏移到派生类对象内部某个父类部分的起始地址。

1. 派生类指针赋值给基类指针

【问题】

如果new一个派生类对象,把new之后的指针赋值给基类指针,那这个基类指针指向哪里,一定是这个派生类对象的起始地址吗?

【答案】

赋值给基类指针以后,这个基类指针会指向派生类对象中这个基类部分的起始地址,编译器自动进行了指针偏移操作。

1.1. new 与单继承

示例代码:

#include <iostream>

class Base1 {
public:
    int base1Value;

    // Constructor for Base1
    Base1(int b1) : base1Value(b1) {}

};

class Derived : public Base1 {
public:
    int derivedValue;

    // Constructor for Derived
    Derived(int b1, int d) : Base1(b1), derivedValue(d) {}

};

int main() {
    std::cout << "sizeof(Base1): " << sizeof(Base1) << std::endl;
    std::cout << "sizeof(Derived): " << sizeof(Derived) << std::endl;

    // Create a Derived object and assign it to a Base2 pointer
    Base1* b1ptr = new Derived(10, 30);

    // Print address pointed to by b1ptr
    std::cout << "b1ptr address: " << b1ptr << std::endl;

    Derived *dptr = static_cast<Derived *>(b1ptr);

    std::cout << "dptr address: " << dptr << std::endl;

    // Clean up
    delete dptr;
    return 0;
}

运行结果:

$ ./layout_part3_singleParent
sizeof(Base1): 4
sizeof(Derived): 8
b1ptr address: 0x11f92c0
dptr address: 0x11f92c0

可见,由于只有一个基类,赋值以后的基类指针就是派生类对象的起始地址。

1.2. new 与多继承

示例代码:

#include <iostream>

class Base1 {
public:
    int base1Value;

    // Constructor for Base1
    Base1(int b1) : base1Value(b1) {}

};

class Base2 {
public:
    int base2Value;

    // Constructor for Base2
    Base2(int b2) : base2Value(b2) {}

};

class Derived : public Base1, public Base2 {
public:
    int derivedValue;

    // Constructor for Derived
    Derived(int b1, int b2, int d) : Base1(b1), Base2(b2), derivedValue(d) {}

};


int main() {
    std::cout << "sizeof(Base1): " << sizeof(Base1) << std::endl;
    std::cout << "sizeof(Base2): " << sizeof(Base2) << std::endl;
    std::cout << "sizeof(Derived): " << sizeof(Derived) << std::endl;

    // Create a Derived object and assign it to a Base1 pointer
    Base1* b1ptr = new Derived(10, 20, 30);

    // Print address pointed to by b1ptr
    std::cout << "b1ptr address: " << b1ptr << std::endl;

    Derived *dptr1 = static_cast<Derived *>(b1ptr);
    std::cout << "dptr1 address: " << dptr1 << std::endl;

    // Create a Derived object and assign it to a Base2 pointer
    Base2* b2ptr = new Derived(40, 50, 60);

    // Print address pointed to by b2ptr
    std::cout << "b2ptr address: " << b2ptr << std::endl;

    Derived *dptr2 = static_cast<Derived *>(b2ptr);
    std::cout << "dptr2 address: " << dptr2 << std::endl;

    // Clean up
    delete dptr1;
    return 0;
}

运行结果:

$ ./layout_part3_multiParent
sizeof(Base1): 4
sizeof(Base2): 4
sizeof(Derived): 12
b1ptr address: 0xc052c0
dptr1 address: 0xc052c0
b2ptr address: 0xc052e4
dptr2 address: 0xc052e0

内存布局:

当执行Base1* b1ptr = new Derived(10, 20, 30); 时

当执行Base2* b2ptr = new Derived(40, 50, 60); 时

b2ptr是如何能够指向派生类对象中的Base2部分的起始地址的呢?

从汇编一探究竟, Base2* b2ptr = new Derived(40, 50, 60);对应的汇编:

有偏移4的操作:lea    0x4(%rbx),%rax

1.3. 派生类对象指针直接赋值给基类指针

参照上面多继承的例子稍作修改:

    Derived* dptr = new Derived(10, 20, 30);

    std::cout << "dptr address: " << dptr << std::endl;

    Base1 *b1ptr = dptr;
    Base2 *b2ptr = dptr;

    std::cout << "b1ptr address: " << b1ptr << std::endl;
    std::cout << "b2ptr address: " << b2ptr << std::endl;

运行结果:

dptr address: 0x1c6e2c0
b1ptr address: 0x1c6e2c0
b2ptr address: 0x1c6e2c4

2. 函数参数传递

继续修改上面的多继承例子:

void f1(Base1 *b1ptr) {
    std::cout << "b1ptr is: " << b1ptr << std::endl;
}

void f2(Base2 *b2ptr) {
    std::cout << "b2ptr is: " << b2ptr << std::endl;
}

int main() {

    Derived* dptr = new Derived(10, 20, 30);
    std::cout << "dptr address: " << dptr << std::endl;

    f1(dptr);
    f2(dptr);

    // Clean up
    delete dptr;
    return 0;
}

运行结果:

dptr address: 0x17a02c0
b1ptr is: 0x17a02c0
b2ptr is: 0x17a02c4

与指针直接赋值的效果是一致的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值