详解C++类与对象:构造函数、析构函数与拷贝构造

温以宁
温以宁
发布时间:2026-10-16| 4人浏览过
原创

C++中的类与对象通过特殊的成员函数管理生命周期。本文详解构造函数(初始化)、析构函数(清理)与拷贝构造函数(复制),剖析系统默认行为与自定义实现的差异,重点解决参数名冲突、浅拷贝双重释放等常见错误,提供安全的资源管理与对象拷贝方案。

一. 构造函数

构造函数是类中一种特殊的成员函数,其名称必须与类名完全相同。它的主要职责是在创建对象时自动调用,确保每个数据成员都有一个合适的初始值。在对象的生命周期内,构造函数只被调用一次。与C语言不同,C++允许在对象实例化的同时完成成员变量的初始化。

第1步:理解构造函数的特征

构造函数具备以下核心特征:

  1. 命名规范:函数名必须与类名一致。
  2. 返回值:无返回值类型,甚至不能有 void。
  3. 自动调用:编译器在实例化对象时自动触发,而非手动调用。
  4. 支持重载:可以定义多个同名但参数不同的构造函数。

第2步:系统默认与用户定义的构造行为

当类未显式定义任何构造函数时,编译器会生成一个默认构造函数。需注意:

  • 内置类型(如 int, char)的成员变量不会被初始化,保留随机值。
  • 自定义类型(如结构体或类)的成员变量会自动调用其自身的默认构造函数。

第3步:自定义构造函数的实现

用户通常需要通过自定义构造函数来初始化对象,主要分为无参构造和带参构造。

1. 无参构造
不提供任何参数的构造函数,常用于为所有成员设置统一的默认初始状态。

2. 带参构造与全缺省参数
带参构造函数允许在实例化时指定初始值。为了避免版本混乱,推荐使用全缺省参数的构造函数,它在功能上等同于无参构造和有参构造的结合,且符合“默认构造函数只能有一个”的规则(注:此处指只有一个无参的或全缺省的默认构造函数入口)。

常见问题排查:参数遮蔽(Shadowing)
在使用带参构造时,若形参与成员变量同名,容易引发赋值错误。

class A {
public:
    A(int year) {
        year = year; // 错误!就近原则导致自身赋值
    }
private:
    int year;
};
int main() {
    A a(20);
}

上述代码中,year = year; 执行的是局部变量对自身赋值,导致成员变量 year 仍为随机值。解决该问题的标准做法是使用 this 指针明确指向成员变量:this->year = year;。

推荐写法:
使用全缺省构造函数可以提供灵活的初始化方式:

class Data {
public:
    Data(int year = 2022, int month = 12, int day = 12) {
        _year = year;
        _month = month;
        _day = day;
    }
private:
    int _year, _month, _day;
};

二. 析构函数

当对象生命周期结束时,需要清理其占用的资源(特别是堆内存)。析构函数承担这一责任。

第1步:析构函数的定义与规则

  • 以波浪号 ~ 加上类名命名,例如 ~Stack()。
  • 无参数,无返回值。
  • 一个类只能有一个析构函数。
  • 若未显式定义,编译器生成默认析构函数。
  • 局部对象销毁由编译器处理,析构函数负责清理非栈资源的附属工作。

第2步:动态资源的清理实践

对于在构造函数中使用 malloc 或 new 申请的资源,必须在析构函数中进行释放,以防止内存泄漏。

class Stack {
public:
    Stack(int capacity = 4) {
        _a = (int*)malloc(sizeof(int)*capacity);
        if (_a == nullptr) {
            cout << "malloc fail" << endl;
            exit(-1);
        }
        _top = 0;
        _capacity = capacity;
    }
    // 析构函数清理堆内存
    ~Stack() {
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }
private:
    int* _a;
    int _top;
    int _capacity;
};

注意:析构后最好将指针置为 nullptr,防止悬垂指针引发的二次释放风险。对于内置类型成员,析构函数无需特殊处理;对于自定义类型成员,则会自动调用其析构函数。

三. 拷贝构造函数

当需要使用一个已存在的对象去初始化另一个新对象时,编译器会调用拷贝构造函数。

第1步:拷贝构造的定义与避坑指南

  • 定义:只有一个参数,即对本类类型对象的引用(通常用 const 修饰)。
  • 关键特征:参数必须是引用(&)。
  • 无限递归陷阱:如果采用值传递(Data s),为了实参传递给形参会再次调用拷贝构造函数,导致死循环直至栈溢出。
  • 正确签名:Data(const Data& other)。

第2步:浅拷贝的危害

若用户未显式定义拷贝构造函数,系统会生成默认的“浅拷贝”。这意味着仅进行字节层面的数值拷贝。如果类中包含指针成员,两个对象将共享同一块堆内存。

下面演示包含指针成员的 String 类在浅拷贝下的崩溃场景:

class String {
public:
    String(const char* str = "jack") {
        _str = (char*)malloc(strlen(str) + 1);
        strcpy(_str, str);
    }
    ~String() {
        cout << "~String()" << endl;
        free(_str); // 释放内存
    }
private:
    char* _str;
};
int main() {
    String s;
    String s1(s); // 触发默认浅拷贝
    return 0;
}

程序在返回时会抛出异常。原因分析如下:

1. s 和 s1 的 _str 指向同一块堆内存。

2. s1 先析构,调用 free(_str),内存被释放。

3. s 后析构,再次调用 free(_str),导致对已释放内存的重复操作,引发运行时错误。

第3步:如何实现深拷贝

为防止共享资源被重复释放,必须实现深拷贝(Deep Copy),即为新对象分配独立的内存空间,并复制原内容。

自定义拷贝构造函数的步骤:

  1. 检查源对象的有效性。
  2. 根据源对象的大小/长度重新申请当前对象的内存。
  3. 将源对象的数据拷贝到新内存中。
  4. 利用 this 指针隔离命名冲突。
class String {
public:
    // ... (构造函数略)
    
    // 自定义深拷贝构造函数
    String(const String& other) {
        _str = (char*)malloc(strlen(other._str) + 1);
        strcpy(_str, other._str);
    }
    ~String() {
        free(_str);
    }
private:
    char* _str;
};

这样修改后,s 和 s1 拥有各自独立的堆内存,析构时不会发生冲突。

总结

C++ 类的四个默认成员函数——构造函数、析构函数、拷贝构造函数和赋值运算符重载,共同构成了对象生命周期管理的基石。初学者常犯的错误包括:忽略参数遮蔽导致初始化失败、忘记析构动态内存导致泄漏、以及使用浅拷贝指针导致重复释放。遵循 C++ 的最佳实践(如使用 this 指针、始终对资源管理类实现深拷贝),能显著提升程序的稳定性。

以上就是C++类与对象核心概念的详细内容,更多关于C++面向对象编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。