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
与指针直接赋值的效果是一致的。