1 重写(继承后(拼接基类后))
1.1 非虚函数 同名成员函数 (各自有一个xFunction() 内存 )
#include <iostream>
#include <String>
class BaseClass {
public:void xFunction() {std::cout << "BaseClass::xFunction()\n"; }
};class Subclass1 : public BaseClass {
public:void xFunction() {std::cout << "Subclass1::xFunction()\n";}
};int main() {Subclass1 subclass1;subclass1.xFunction();return 0;
}
输出:
Subclass1::xFunction()
基类对象内存布局:
+-------------+
| 其他成员变量 |
+-------------+
| xFunction() | -> 基类xFunction()函数的代码
+-------------+子类对象内存布局:
+-------------+
| 基类部分 | // 继承而来的基类的数据和方法
+-------------+
| 子类独有成员变量 |
+-------------+
| xFunction() | -> 子类xFunction()函数的代码
+-------------+
运行原则是:
- 子类对象可以直接访问继承而来的基类的数据和方法
- 当子类与基类存在同名函数时,根据就近原则,通过子类对象访问同名函数时,会调用子类自己的同名函数,即这里的Subclass1::xFunction()
- 如果要访问基类中被隐藏的同名函数,需要使用作用域解析运算符,如subclass1.BaseClass::xFunction()
所以在这个例子中,通过subclass1.xFunction()调用时,执行的就是子类中重写的xFunction()函数,输出"Subclass1::xFunction()"。
1.1.2 多次实例化subclass1对象 (就是多次创建应用这个类 开内存)
每个实例化对像的成员变量 内存是单独的
成员函数是多个此类实例对象 公用子类 基类的成员函数 用类:: 指明
成员函数代码区(共享):┌──────────────────────────────┐
│ 成员函数代码 │
│ (包括 BaseClass::xFunction) │
│ (包括 Subclass1::xFunction) │
└──────────────────────────────┘subclass1对象实例1:
┌──────────────────────────────┐
│ 成员变量 │
└──────────────────────────────┘subclass1对象实例2:
┌──────────────────────────────┐
│ 成员变量 │
└──────────────────────────────┘
1.2 虚函数virtual (只有一个xFunction()内存 在基类中, 重写就覆盖)
#include <iostream>
#include <String>
class BaseClass {
public:virtual void xFunction() {std::cout << "BaseClass::xFunction()\n"; }
};class Subclass1 : public BaseClass {
public:void xFunction() {std::cout << "Subclass1::xFunction()\n";}
};int main() {Subclass1 subclass1;subclass1.xFunction();return 0;
}
+------------------------+
| subclass1 |
+------------------------+
| vptr (指向虚函数表) |
+------------------------+
| |
| |
| |
| |
| |
| |
| |
| |
+------------------------++------------------------+
| BaseClass |
+------------------------+
| vtable (虚函数表) |
+------------------------+
| xFunction() |
+------------------------+就是subclass1存的是基类的xFunction()地址的地址?是的,您的理解是正确的。
subclass1中存的是指向基类中xFunction()这个成员函数的指针的地址。
所以它是一个二级指针。
2 多态(子类用基类模板创建/增加不同功能)
#include <iostream>class Operation {
public:virtual int calculate(int a, int b) = 0; // 纯虚函数,需要在子类中实现
};class Addition : public Operation {
public:int calculate(int a, int b) override {return a + b;}
};class Subtraction : public Operation {
public:int calculate(int a, int b) override {return a - b;}
};int main() {Operation* op1 = new Addition(); // 创建加法类对象,并将其指针赋值给基类指针Operation* op2 = new Subtraction(); // 创建减法类对象,并将其指针赋值给基类指针int result1 = op1->calculate(5, 3); // 调用加法类的calculate函数int result2 = op2->calculate(5, 3); // 调用减法类的calculate函数std::cout << "Result1: " << result1 << std::endl; // 输出加法计算结果std::cout << "Result2: " << result2 << std::endl; // 输出减法计算结果delete op1;delete op2;return 0;
}
Operation* op1 = new Addition(); 创建对象并开内存 并给基类一个初始地址,;
然后根据这个地址 基类分配各个成员的空间;
初始化完 基类Operation被改造有成员变量calculate();
子类 只有指向基类的地址 用于找到虚函数 成员变量;
+------------------------+
| Operation |
+------------------------+
| vtable (虚函数表) |
+------------------------+
| calculate() |
+------------------------++------------------------+
| Subtraction |
+------------------------+
| vptr (指向虚函数表) |
+------------------------+
3
3.1
Operation* op1 = new Addition();
3.2 & 取地址符号
Addition add; // 创建命名的 Addition 对象
Operation* op1 = &add; // 将对象的地址赋值给基类指针
是的,Operation* op1 = new Addition();这一行代码是在堆内存上动态分配了一个Addition对象。
具体来说:
new Addition() 在堆内存上创建了一个Addition对象,并返回该对象的地址
Operation* op1 定义了一个Operation类型的指针op1
= 将new Addition()返回的Addition对象的地址赋值给了指针op1
所以,op1指向了堆内存中动态分配的那个Addition对象。
而Addition add这行代码是在栈内存上创建了一个Addition类型的对象add。
两者的区别在于:
- new创建的是在堆内存上的对象
- 直接创建如Addition add的是在栈内存上的对象
堆内存对象的生命周期由程序员控制,栈内存对象的生命周期随着函数的结束而结束。
将Addition对象的地址赋值给Operation*类型的指针op1,实现了基类指针指向子类对象的效果,可以实现多态。这是面向对象编程中非常重要的概念。