虚函数详解参考:
一文读懂C++虚函数的内存模型_gdp c++ 内存模型 虚函数表-优快云博客
上述这2篇文章即可.
-
在C++中,基类是虚函数,子类对象指针赋值给基类对象指针, 编译器怎么知道当前指针是子类对象还是基类对象的指针?
在C++中,当基类包含虚函数时,编译器会在对象内存中创建一个虚函数表(vtable)。每个类(包括基类和子类)都有自己的虚函数表,表中存储了该类的虚函数的地址。当你将子类对象的指针赋值给基类对象的指针时,基类指针实际上指向的是子类对象的内存区域。由于对象内存中包含了虚函数表指针(vptr),编译器能够通过这个指针找到正确的虚函数表,并调用相应的函数。
详细解释
-
虚函数表(vtable)和虚函数指针(vptr):
- 当一个类声明了虚函数时,编译器会为该类生成一个虚函数表(vtable),虚函数表中存储了该类的所有虚函数的地址。
- 每个对象在内存中会有一个隐藏的指针,指向它所属类的虚函数表,这个指针称为虚函数指针(vptr)。
-
子类对象的虚函数表:
- 子类继承了基类的虚函数表,并且可以覆盖基类中的虚函数。当子类覆盖基类的虚函数时,子类的虚函数表中相应的位置会存储子类的虚函数的地址。
-
基类指针指向子类对象:
- 当你将子类对象的指针赋值给基类对象的指针时,基类指针实际上指向的是子类对象的内存区域。
- 由于子类对象的内存中包含了虚函数指针(vptr),这个指针指向子类的虚函数表。
-
调用虚函数:
- 当通过基类指针调用虚函数时,编译器会通过虚函数指针(vptr)找到实际的虚函数表(vtable),并调用表中相应的函数。
- 由于基类指针实际指向的是子类对象的内存区域,虚函数指针指向的是子类的虚函数表,因此会调用子类的虚函数。
#include <iostream>
class Base {
public:
virtual void show() {
std::cout << "Base class show function" << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived class show function" << std::endl;
}
};
int main() {
Base* basePtr;
Derived derivedObj;
basePtr = &derivedObj;
// 调用虚函数
basePtr->show(); // 输出: Derived class show function
return 0;
}
解释
- **基类
Base
**:
class Base {
public:
virtual void show() {
std::cout << "Base class show function" << std::endl;
}
};
- **子类
Derived
**:
class Derived : public Base {
public:
void show() override {
std::cout << "Derived class show function" << std::endl;
}
};
- 在
main
函数中:
Base* basePtr;
Derived derivedObj;
basePtr = &derivedObj;
// 调用虚函数
basePtr->show(); // 输出: Derived class show function
在这个示例中,basePtr
是一个基类指针,指向 derivedObj
(一个子类对象)。当通过 basePtr
调用 show
函数时,编译器会通过 derivedObj
的虚函数指针找到 Derived
类的虚函数表,并调用 Derived
类的 show
函数。因此,输出结果是 "Derived class show function"。
-
为什么子类对象指针可以转换为基类对象存储呢?
在C++中,子类对象指针可以转换为基类对象指针,这是因为子类对象包含基类对象的所有成员和行为。这种转换称为向上转换(upcasting),它是安全且隐式的。以下是详细的解释:
继承和对象模型
当一个类(子类)继承自另一个类(基类)时,子类不仅继承了基类的所有成员变量和成员函数,还可以添加自己的成员变量和成员函数。从内存布局的角度来看,子类对象在内存中包含了一个基类对象的内存布局,然后在此基础上添加子类特有的部分。
向上转换(Upcasting)
向上转换指的是将子类对象的指针或引用转换为基类对象的指针或引用。这种转换是隐式的,不需要显式的类型转换操作符。因为子类对象包含了基类对象的所有成员,所以编译器可以安全地进行这种转换。
为什么向上转换是安全的
-
内存布局:
- 子类对象的内存布局包含基类对象的内存布局。因此,当你将子类对象的指针转换为基类对象的指针时,基类部分的内存布局是保持一致的,编译器可以正确地访问基类的成员。
-
多态性:
- C++的多态性是通过虚函数实现的。当基类包含虚函数时,子类可以覆盖这些虚函数。通过基类指针调用虚函数时,实际调用的是子类的实现,这使得向上转换在多态性中非常有用。
示例代码
#include <iostream>
class Base {
public:
virtual void show() {
std::cout << "Base class show function" << std::endl;
}
};
cla