✅1. 构造函数调用顺序(完整版本)
- 基类构造函数
- 如果没有显式写初始化列表,默认调用基类的默认构造函数。
- 如果初始化列表中显式调用了某个基类构造函数,则用该构造函数。
- 成员变量构造 (按照它们在类中声明的顺序 ,不是初始化列表顺序)。
- 派生类构造函数体执行。
💡 例子演示:
cpp
#include <iostream>
class Member {
public:
Member() { std::cout << "Member constructor\n"; }
};
class Base {
public:
Base() { std::cout << "Base constructor\n"; }
};
class Derived : public Base {
Member m; // 成员变量
public:
Derived() { std::cout << "Derived constructor\n"; }
};
int main() {
Derived d;
return 0;
}
✅ 输出结果:
Base constructor
Member constructor
Derived constructor
🔄 调用顺序解释:
步骤 | 执行的构造函数 | 原因 |
---|---|---|
1 | Base() |
基类构造函数最先执行 |
2 | Member() |
成员变量构造,按声明顺序 |
3 | Derived() 构造函数体 |
最后才进入子类构造函数的函数体 |
🧠 小贴士
- 成员变量构造顺序只跟它们在类里出现的顺序有关,跟你写在初始化列表里的顺序无关。
- 构造顺序是:"从外到里" ,析构顺序则是反过来:"从里到外"。
2.什么是虚析构函数?为什么需要它?
回答要点:
-
定义 :在基类中将析构函数声明为
virtual
,以确保通过基类指针删除派生类对象时,能正确调用派生类的析构函数。 -
很重要!!!如果一个类打算被继承,一定要让它的析构函数为 virtual
-
用途:
- 防止内存泄漏。
- 确保派生类的资源被正确释放。
-
不使用虚析构函数的风险:
- 仅调用基类析构函数,导致派生类资源未释放。
示例:
如果BaseA
的析构不写成虚析构,则主函数开辟子类对象赋值给基类指针,以后delete
基类指针的时候会发现没有析构子类
cpp
class BaseA{
public:
BaseA(std::string name):_name(name){
std::cout << "BaseA()" << std::endl;
}
~BaseA(){
std::cout << "~BaseA()" << std::endl;
}
private:
std::string _name;
};
class DerivedA: public BaseA {
public:
DerivedA(std::string name,std::string num) :
BaseA(name), _num(num) {
std::cout << "DerivedA()" << std::endl;
}
~DerivedA(){
std::cout << "~DerivedA()" << std::endl;
}
private:
std::string _num;
};
主函数回收内存
cpp
BaseA* base = new DerivedA("zack","1002");
delete base;
会看到只调用了基类BaseA
的析构函数。
当BaseA的析构改为虚析构的时候,才会回收子类DerivedA
cpp
class BaseA{
public:
BaseA(std::string name):_name(name){
std::cout << "BaseA()" << std::endl;
}
virtual ~BaseA(){
std::cout << "~BaseA()" << std::endl;
}
private:
std::string _name;
};