C++构造与析构:对象的生与死

一、对象的生命周期

在C++中,对象从创建到销毁经历两个关键阶段:

  • 出生:构造函数负责创建对象并初始化成员变量

  • 死亡:析构函数负责清理资源并进行善后工作

二、构造函数

1. 什么是构造函数?

构造函数是类中一种特殊的成员函数,在对象被创建时自动调用,用于初始化对象的成员变量。

cpp 复制代码
class Student {
private:
    string name;
    int age;
public:
    // 构造函数:函数名和类名相同,无返回类型
    Student(string n, int a) {
        name = n;
        age = a;
    }
};

int main() {
    Student s1("张三", 18);  // 自动调用构造函数
    return 0;
}

2. 构造函数的特点

特点 说明
函数名 与类名相同
返回类型 无返回类型 (不是 void,是根本不写)
调用时机 对象创建时自动调用,对象不能手动调用构造函数
调用次数 一个对象只能调用一次构造函数
重载 可以定义多个构造函数(参数列表不同)
缺省构造函数 如果没写任何构造函数,编译器自动生成一个无参构造函数

3. 构造函数的三种写法

cpp 复制代码
class Student {
private:
    string name;
    int age;
public:
    // 1. 无参构造函数
    Student() {
        name = "未知";
        age = 0;
    }
    
    // 2. 带参数的构造函数
    Student(string n, int a) {
        name = n;
        age = a;
    }
    
    // 3. 初始化列表(推荐)
    Student(string n, int a) : name(n), age(a) {
        // 函数体可以为空
    }
};

推荐使用初始化列表:效率更高,直接初始化成员变量,而不是先默认初始化再赋值。

4. 缺省构造函数

如果类中没有定义任何构造函数,编译器会自动生成一个缺省构造函数(无参数,函数体为空)。

cpp 复制代码
class Point {
private:
    int x, y;
    // 编译器自动生成:Point() {}
};

int main() {
    Point p;  // 调用缺省构造函数,但 x 和 y 是随机值
}

注意

  • 一个类只有一个缺省构造函数

  • 缺省构造函数是编译器自动生成的无参构造函数

  • 自己写的无参构造函数不叫"缺省构造函数",叫"无参构造函数"

缺省函数 vs 缺省参数

这两个概念容易混淆:

术语 含义 示例
缺省函数 编译器自动生成的、无参数的函数 没写构造函数时,编译器生成 类名() {}
缺省参数 函数参数有默认值 void func(int a = 10);

说明

  • 缺省函数 = 系统自动生成的、无参数的函数

  • 缺省参数 = 函数参数有默认值

  • 析构函数不能有参数,所以不存在"缺省参数"的说法

cpp 复制代码
class Test {
public:
    Test(int a = 10) {}  // 这是缺省参数,不是缺省函数
};

5. 构造函数可以在类外定义

构造函数可以在类中声明,在类外定义。定义时需要用 类名:: 指明所属类。

cpp 复制代码
class Student {
private:
    string name;
    int age;
public:
    Student(string n, int a);  // 声明
};

// 类外定义
Student::Student(string n, int a) : name(n), age(a) {}

说明 :将声明和定义分开,可以让类的接口更清晰,实现细节隐藏在 .cpp 文件中。

6. 构造函数可以是私有的吗?

可以。构造函数可以是 privateprotectedpublic,但绝大多数情况设为 public,否则外部无法创建对象。

cpp 复制代码
class Singleton {
private:
    Singleton() {}  // 私有构造函数,外部无法创建对象
public:
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }
};

三、存储空间与对象的关系

1. 有空间不一定有对象,有对象一定有空间

内存空间是客观存在的,但只有当我们在这块空间上构造对象时,它才成为一个"对象"。

2. 空类占多少字节?

cpp 复制代码
class Empty { };
cout << sizeof(Empty) << endl;  // 输出 1

为什么是1字节?

如果空类占0字节,两个对象在内存中就无法区分------它们的地址会相同。为了确保每个对象有唯一的地址,编译器给空类分配1字节作为占位符。这个字节只用来标识对象的存在,不存储任何数据。

四、构造函数完整示例

cpp 复制代码
#include <iostream>
using namespace std;

class CDate {
private:
    int year;
    int month;
    int day;
public:
    // 无参构造函数
    CDate() : year(1), month(1), day(1) {}
    
    // 带参构造函数
    CDate(int y, int m, int d) : year(y), month(m), day(d) {}
    
    void PrintDate() const {
        printf("%04d/%02d/%02d\n", year, month, day);
    }
};

int main() {
    CDate dt1;                // 调用无参构造函数
    CDate dt2(2025, 7, 24);   // 调用带参构造函数
    
    dt1.PrintDate();  // 输出 0001/01/01
    dt2.PrintDate();  // 输出 2025/07/24
    
    return 0;
}

说明dt1 使用无参构造函数,年月日被初始化为 (1,1,1);dt2 使用带参构造函数,用传入的值初始化。两种方式展示了构造函数的重载用法。

五、析构函数

1. 什么是析构函数?

析构函数是对象生命周期结束时自动调用 的成员函数,用于清理资源(如释放动态分配的内存、关闭文件等)。

cpp 复制代码
class CGoods {
private:
    char* name;
public:
    // 构造函数:分配资源
    CGoods(const char* n) {
        name = new char[strlen(n) + 1];
        strcpy(name, n);
    }
    
    // 析构函数:释放资源
    ~CGoods() {
        delete[] name;  // 释放动态分配的内存
    }
};

2. 析构函数的特点

特点 说明
函数名 ~类名,如 ~CGoods()
返回类型 无返回类型(也不能是 void
参数 不带任何参数
个数 一个类有且仅有一个析构函数
调用时机 对象销毁时自动调用(作用域结束、delete等)
缺省析构函数 如果没写,编译器自动生成一个空函数体的析构函数

3. 缺省析构函数

如果类中没有定义析构函数,编译器会自动生成一个缺省析构函数(函数体为空)。

cpp 复制代码
class Point {
    int x, y;
    // 编译器自动生成:~Point() {}
};

注意

  • 缺省析构函数不释放动态分配的内存,如果类中有指针成员,必须自己写析构函数

  • 析构函数不能有参数,所以不存在"缺省参数"的说法

4. 对象可以调用析构函数吗?

对象不能调用构造函数 ,但可以手动调用析构函数(一般不推荐)。

cpp 复制代码
class Test {
public:
    ~Test() {
        cout << "析构函数被调用" << endl;
    }
};

int main() {
    Test t;
    t.~Test();  // ✅ 可以手动调用,但之后对象还在,再次销毁会出问题
    return 0;
}

不建议手动调用:对象销毁时析构函数会被再次调用,导致重复释放等错误。

六、构造函数 vs 析构函数

对比项 构造函数 析构函数
函数名 类名 ~类名
参数 可以有 无参数
返回类型
重载 可以(多个构造函数) 不可以(只有一个)
调用时机 对象创建时 对象销毁时
调用次数 每个对象一次 每个对象一次
手动调用 ❌ 不能 ✅ 可以(但不推荐)
缺省版本 没写任何构造函数时生成 没写析构函数时生成

七、完整示例:Point 和 Line 类

下面的 Point 类演示了构造函数的两种写法:无参构造函数和带参构造函数。

cpp 复制代码
class Point {
private:
    float xpos;
    float ypos;
public:
    Point() : xpos(0.0), ypos(0.0) {}
    Point(float x, float y) : xpos(x), ypos(y) {}
    
    void setX(float x) { xpos = x; }
    void setY(float y) { ypos = y; }
    float getX() const { return xpos; }
    float getY() const { return ypos; }
    void Printpos() const {
        printf("x:%.2f, y:%.2f\n", xpos, ypos);
    }
};

Line 类演示了两种构造函数:无参构造函数和四点坐标初始化。

cpp 复制代码
#include <cmath>

class Line {
private:
    Point a;
    Point b;
public:
    // 无参构造函数:两个端点都是 (0,0)
    Line() : a(0.0, 0.0), b(0.0, 0.0) {}
    
    // 带参构造函数:用四个坐标初始化
    Line(float a_x, float a_y, float b_x, float b_y) 
        : a(a_x, a_y), b(b_x, b_y) {}
    
    const Point& getPointa() const { return a; }
    const Point& getPointb() const { return b; }
    
    float get_length() const {
        float dx = a.getX() - b.getX();
        float dy = a.getY() - b.getY();
        return sqrt(dx * dx + dy * dy);
    }
    
    void PrintLine() const {
        printf("端点A: ");
        a.Printpos();
        printf("端点B: ");
        b.Printpos();
        printf("长度: %.2f\n", get_length());
    }
};

main 函数展示了两种构造方式的调用。

cpp 复制代码
int main() {
    Line l1;                                    // 无参构造函数
    printf("=== line1 ===\n");
    l1.PrintLine();

    Line l2(1.2, 2.3, 3.4, 4.5);               // 四点初始化
    printf("\n=== line2 ===\n");
    l2.PrintLine();

    return 0;
}

示例说明

  • l1:无参构造函数,两个端点都初始化为 (0,0)

  • l2:四点坐标初始化,用传入的四个坐标值初始化两个端点

八、总结

知识点 核心要点
构造函数作用 创建对象 + 初始化成员变量
构造函数特点 函数名=类名,无返回类型,可重载,自动调用,不能手动调用
构造函数定义位置 可在类内定义,也可在类外定义(类名::类名() {}
缺省构造函数 没写任何构造函数时编译器自动生成(无参,函数体为空)
缺省函数 vs 缺省参数 缺省函数是编译器自动生成的无参函数;缺省参数是函数参数有默认值
私有构造函数 用于单例模式等场景,禁止外部直接创建对象
初始化列表 推荐写法,效率更高
空类大小 占1字节,确保每个对象有唯一地址
析构函数作用 清理资源(释放动态内存、关闭文件等)
析构函数特点 函数名=~类名,无参数,无返回类型,不可重载,可手动调用
缺省析构函数 没写析构函数时编译器自动生成(空函数体)
相关推荐
REDcker2 小时前
C++ 多线程内存模型与 memory_order 详解
java·c++·spring
AbandonForce2 小时前
STL list
开发语言·c++
水饺编程2 小时前
第4章,[标签 Win32] :SysMets3 程序讲解05,水平滚动
c语言·c++·windows·visual studio
lihao lihao2 小时前
进程地址空间
数据结构·c++·算法
Byte不洛2 小时前
LeetCode双指针经典题
c++·算法·leetcode·双指针
Tanecious.2 小时前
蓝桥杯备赛:Day7- P10424 [蓝桥杯 2024 省 B] 好数
c++·蓝桥杯
Albert Edison2 小时前
【C++11】特殊类设计
开发语言·c++·单例模式·饿汉模式·懒汉模式
代码改善世界2 小时前
【C++初阶】vector 核心接口和模拟实现
开发语言·c++
今晚打老虎2 小时前
限时回归了
c++