C++类的特殊成员函数:构造、拷贝构造与析构函数详解

目录

​编辑一、构造函数 

二、拷贝构造函数 

三、析构函数 


在C++ 编程中,类的特殊成员函数扮演着至关重要的角色,它们负责对象的创建、复制以及销毁过程。本文将深入探讨构造函数、拷贝构造函数和析构函数的概念、特性及应用场景,并结合代码示例进行详细说明。
 


一、构造函数
 


1. 概念
构造函数是一个特殊的成员函数,名字与类名相同。在创建类类型对象时由编译器自动调用,主要任务是初始化对象的数据成员确保每个数据成员都有一个合适的初始值,并且在对象的整个生命周期内只调用一次。
 

2. 特性
 
- 函数名与类名相同:这是构造函数的显著特征,通过函数名就能明确它所属的类。
 
- 无返回值:构造函数不返回任何值,它专注于对象的初始化工作。
 
- 自动调用:当对象实例化时,编译器会自动调用对应的构造函数。
 
- 可重载:可以定义多个参数列表不同的构造函数,以满足不同的初始化需求,这就是构造函数的重载。
 
3. 示例代码
 

cpp
  
class Date {
public:
    // 无参构造函数
    Date() {
        _year = 1900;
        _month = 1;
        _day = 1;
    }

    // 带参构造函数
    Date(int year, int month, int day) {
        _year = year;
        _month = month;
        _day = day;
    }

private:
    int _year;
    int _month;
    int _day;
};

void TestDate() {
    Date d1; // 调用无参构造函数
    Date d2(2015, 1, 1); // 调用带参构造函数
}
 


 
1. 要点
如果类中没有显式定义(只进行简单声明)构造函数,C++ 编译器会自动生成一个无参的默认构造函数。但一旦用户显式定义了任何构造函数,编译器将不再生成默认构造函数。同时,使用无参构造函数创建对象时,对象后面不要跟括号,否则会被当成函数声明。
 


二、拷贝构造函数
 


1. 概念
拷贝构造函数是构造函数的一种重载形式,只有单个形参,该形参是对本类类型对象的引用(一般常用  const  修饰),在使用已存在的类类型对象创建新对象时由编译器自动调用。
 
2. 特性
 
- 构造函数的重载形式:本质上是构造函数的一种特殊情况,用于对象的复制操作。
 
- 参数要求:参数只有一个且必须是类类型对象的引用。使用传值方式会引发无穷递归调用,因为传值时会调用拷贝构造函数来复制实参,从而陷入无限循环,编译器会直接报错。
 
- 默认生成:若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对对象按内存存储按字节序完成拷贝,即浅拷贝(值拷贝)。
 
3. 示例代码
 

cpp
  
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
    }

    // 正确的拷贝构造函数写法
    Date(const Date& d) {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    Date d1;
    Date d2(d1); // 调用拷贝构造函数
    return 0;
}
 


 
1. 要点
对于简单的类,默认的拷贝构造函数(浅拷贝)可能已经足够。但当类中涉及资源申请(如动态内存分配)时,浅拷贝会带来问题,比如多个对象指向同一块资源,在对象析构时可能导致资源重复释放或内存泄漏,此时就需要显式实现深拷贝构造函数。
 


三、析构函数
 


1. 概念
析构函数与构造函数功能相反,它不是完成对对象本身的销毁(局部对象销毁工作由编译器完成),而是在对象销毁时自动调用,用于完成对象中资源的清理工作,比如释放动态分配的内存等。
 
2. 特性
 
- 函数名:在类名前加上字符  ~  作为函数名。
 
- 无参数无返回值类型:析构函数不接受参数,也不返回任何值。
 
- 唯一性:一个类只能有一个析构函数,若未显式定义,系统会自动生成默认的析构函数,且析构函数不能重载。
 
- 自动调用:当对象生命周期结束时,C++ 编译系统会自动调用析构函数。
 
3. 示例代码

 
cpp
  
class Stack {
public:
    Stack(size_t capacity = 3) {
        _array = (DataType*)malloc(sizeof(DataType) * capacity);
        if (NULL == _array) {
            perror("malloc申请空间失败!!!");
            return;
        }
        _capacity = capacity;
        _size = 0;
    }

    ~Stack() {
        if (_array) {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    DataType* _array;
    int _capacity;
    int _size;
};

void TestStack() {
    Stack s;
    s.Push(1);
    s.Push(2);
}
 


 
1. 要点
如果类中没有申请资源,析构函数可以不写,直接使用编译器生成的默认析构函数即可,比如简单的日期类  Date  。但当类中有资源申请时,必须显式定义析构函数来释放资源,否则会造成资源泄漏,像  Stack  类这种涉及动态内存分配的情况。
 
总之,构造函数、拷贝构造函数和析构函数在C++ 类的对象生命周期管理中起着关键作用,正确理解和使用它们,能确保程序的正确性和资源管理的有效性。