C++ 构造函数、析构函数、虚函数、虚析构

一、构造函数 & 析构函数 基础

1. 构造函数

语法规则
  • 名字和类名完全一样
  • 无返回值、不用写 void/int
  • 对象创建瞬间自动调用
  • 作用:初始化成员、申请资源、配置初始化
示例
cpp 复制代码
#include <iostream>
using namespace std;

class Person {
public:
    // 构造函数:和类名同名
    Person() {
        cout << "构造函数:对象创建了,做初始化\n";
    }
};

int main() {
    Person p; // 自动调用构造函数
    return 0;
}

2. 析构函数

语法规则
  • 固定标准写法:~类名()
  • ~ 是语法规定,代表「构造的反向操作」
  • 无返回值、无参数、不能重载
  • 对象销毁 / delete自动调用
  • 作用:释放内存、关文件、清理资源
示例
cpp 复制代码
class Person {
public:
    Person() {
        cout << "构造:对象出生\n";
    }
    // 标准析构函数写法
    ~Person() {
        cout << "析构:对象销毁,清理资源\n";
    }
};

int main() {
    Person p; // 构造执行
    return 0;  // 函数结束,p 销毁,自动执行析构
}

3. 为什么 C++ 要有构造/析构?对比 C / Java / Python

  1. C 语言
    没有类、没有对象生命周期,只能手动写 init()free(),没有语法级别的构造析构。
c 复制代码
// C 只能手动初始化、手动释放
typedef struct { int age; } Person;
void PersonInit(Person* p) { p->age = 18; }
void PersonFree(Person* p) { /* 手动清理 */ }
  1. Java

    有构造方法,没有析构 ;靠 GC 垃圾回收 自动清理内存,不用程序员手动释放。

  2. Python

    构造 __init__,析构 __del__ 几乎不用;靠引用计数+GC自动管内存。

  3. C++ 独有原因

    手动 new/delete 管内存,没有自动GC;

    必须用构造初始化、析构自动清理,绑定对象生命周期,防内存泄漏。


二、虚函数 核心概念 + 示例

1. 什么是虚函数

成员函数前面加 virtual虚函数
虚析构本质也是虚函数,共用同一套虚表机制。

2. 虚表机制 通俗理解

  • 有虚函数的类,编译器自动生成一张虚表 vtable(存所有虚函数地址)
  • 每个对象暗藏一个 vptr 虚表指针,指向自己真实类型的虚表
  • 非虚函数:编译静态绑定,只看指针类型
  • 虚函数:运行动态绑定,看对象真实类型

3. 虚函数经典示例(多态+重写)

父类形状,子类圆形、方形:

cpp 复制代码
class Shape {
public:
    // 虚函数
    virtual void draw() {
        cout << "绘制通用形状\n";
    }
};

// 子类:圆形
class Circle : public Shape {
public:
    // 重写虚函数
    void draw() override {
        cout << "绘制圆形\n";
    }
};

// 子类:方形
class Square : public Shape {
public:
    void draw() override {
        cout << "绘制方形\n";
    }
};
测试 1:父类指针指向子类对象
cpp 复制代码
int main() {
    // 父类指针 装 子类对象
    Shape* s1 = new Circle();
    Shape* s2 = new Square();

    s1->draw(); // 绘制圆形
    s2->draw(); // 绘制方形

    delete s1;
    delete s2;
    return 0;
}

👉 虚函数作用:父类指针调用,自动执行子类重写的逻辑

测试 2:去掉 virtual 会怎样?

virtual 删掉,输出变成:

复制代码
绘制通用形状
绘制通用形状

只认父类指针类型,不认对象真身。


4 为什么不用 Circle* 非要用 Shape*

单个子类:直接用子类指针没问题
cpp 复制代码
Circle* c = new Circle();
c->draw();
多个子类:父类指针统一批量管理(核心价值)
cpp 复制代码
int main() {
    // 一个父类数组,装所有子类
    Shape* arr[] = {
        new Circle(),
        new Square()
    };

    // 统一循环调用,不用逐个写逻辑
    for (auto s : arr) {
        s->draw();
    }

    return 0;
}

好处:新增三角形、星星,不用改循环调度代码,符合开闭原则。

5. 虚函数两大内部关联功能

  1. 允许子类重写父类虚函数,实现自己业务
  2. 配合父类指针,实现多态统一调度
    二者靠同一张虚表机制实现,不是两个独立功能。

三、虚析构函数 原理 + 完整示例

1. 不加虚析构的坑(内存泄漏演示)

cpp 复制代码
class Base {
public:
    ~Base() { // 普通析构,不是虚析构
        cout << "父类析构\n";
    }
};

class Son : public Base {
public:
    ~Son() {
        cout << "子类析构\n";
    }
};

int main() {
    Base* p = new Son();
    delete p; // 只执行父类析构,子类析构没执行!
    return 0;
}

输出:

复制代码
父类析构

👉 子类资源完全没清理,内存泄漏

2. 改成虚析构,问题解决

cpp 复制代码
class Base {
public:
    // 虚析构函数
    virtual ~Base() {
        cout << "父类析构\n";
    }
};

class Son : public Base {
public:
    ~Son() {
        cout << "子类析构\n";
    }
};

int main() {
    Base* p = new Son();
    delete p; 
    return 0;
}

输出:

复制代码
子类析构
父类析构

👉 先走子类析构,再走父类析构,清理干净。

3. 底层原理总结

  • 析构加 virtual → 进入虚表
  • delete 父类指针 时,走虚表动态绑定
  • 找到真实子类析构执行,再自动向上执行父类析构

4. 最佳实践铁律 + 规范写法

  1. 只要类会被继承当基类,析构必须写 virtual
  2. 不被继承的工具类,不用虚析构,省虚表开销
  3. 现代 C++ 标准写法:
cpp 复制代码
virtual ~Base() = default;

四、全篇终极总结

  1. 构造函数 :和类名同名,对象出生初始化;不能是虚函数
  2. 析构函数 :固定 ~类名() 写法,对象销毁自动清理;~ 代表构造逆操作。
  3. C++ 独有构造析构:因为手动管内存无GC,C/Java/Python 不需要这套机制。
  4. 虚函数 :依靠虚表+虚表指针 ,实现子类重写 + 父类指针多态调用
  5. 父类指针装子类:为了批量统一管理多子类,不用重复写调度代码。
  6. 虚析构 :本质就是虚函数,解决父类指针delete子类对象时内存泄漏
  7. 口诀
    非虚看指针,虚函数看真身;
    可继承基类,析构必虚。
相关推荐
想唱rap1 小时前
应用层协议与序列化
linux·运维·服务器·网络·数据结构·c++·算法
北山有鸟1 小时前
IS_ERR 判断出错后,再用 PTR_ERR 把它强制转换回 int 型的错误码作为函数的返回值。
java·开发语言
许长安1 小时前
protobuf 使用详解
c++·经验分享·笔记·中间件
格林威1 小时前
工业视觉检测:提供可视化UI调试工具的实现方式是什么?
开发语言·人工智能·数码相机·ui·计算机视觉·视觉检测·工业相机
Soley1 小时前
用 Boost.Log 封装一个更顺手的 C++17 日志库:GoodLog
c++
phltxy1 小时前
深度解析:Spring Cloud Gateway 从入门到实战
java·开发语言
HAPPY酷2 小时前
从Public到Private:UE5 C++类创建路径差异全解析
java·c++·ue5
无敌昊哥战神2 小时前
【LeetCode 37】解数独 (Sudoku Solver) —— 回溯法详解 (Python/C/C++)
c语言·c++·python·算法·leetcode
AI进化营-智能译站2 小时前
ROS2 C++开发系列08-传感器数据缓存与指令解析方式之数组、向量与字符串实战
开发语言·c++·缓存·ai