❀构造函数
我们先来看原始的Date类:
class Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Init(2023, 4, 25); d1.Print(); Date d2; d2.Init(2022, 4, 25); d2.Print(); return 0; }
上面的Date类实现中,每创建一个对象就得对对象Init一次,这样就显得很麻烦,而且我们还经常会忘记初始化。那么有没有什么办法能够不用每次都Init呢?答案是有的,那就是我们今天所要介绍的主题【构造函数】。
构造函数是一个特殊的成员函数,他的名字和类的名字相同,创建类的对象的时候编译器会自动调用该函数,以保证对每个数据成员都有初始值,并且在整个对象生命周期中只出现一次。
构造函数的特性
构造函数是特殊的成员函数,虽然名字叫做构造函数,但他的任务可不是为对象创建而开空间,他的主要任务是初始化对象。
特征:
- 函数名与类名相同。
- 无返回值(也不需要写void)
- 对象实例化的时候编译器会自动调用对应的构造函数。
- 构造函数可以重载。
class Date { public: // 1.无参构造函数 Date() {} // 2.带参构造函数 Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; int main() { Date d1; // 调用无参构造函数 Date d2(2015, 1, 1); // 调用带参的构造函数 // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明 // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象 // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?) Date d3(); return 0; }
5. 如果类中没有显示定义构造函数,则C++编译器会自动生成一个午餐的默认构造函数,一旦用户显示定义百年一起将不再生成。
class Date { public: // 如果用户显式定义了构造函数,编译器将不再生成 void Print() { cout << _year << "-" <<_month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Print(); return 0; }
我们可以看到自动生成无参的默认构造函数,并没有对变量进行初始化,打印出来的是随机值。
6.我们不写,编译器自动生成的默认构造函数,内置类型不做处理,自定义类型会去调用他自己的默认构造。
1.内置类型/基本类型,语言本身定义的基础类型例如:char/int/double/指针变量等。
2.自定义类型。(struct/class等定义的类型)
3.有些编译器会自己也会处理内置类型,但那是个性化行为,并不是所有的编译器都会处理。
【注意】:C++11中针对内置类型成员不初始化的缺陷,又打了个补丁,即:内置类型成员变量在类中声明时可以给默认值。
class Date { public: // 如果用户显式定义了构造函数,编译器将不再生成 Date(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year=2023; int _month=4; int _day=28; }; int main() { Date d1; d1.Print(); return 0; }
7.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能由一个。
【注意】:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
class Date { public: Date() { _year = 1900; _month = 1; _day = 1; } Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; int main() { Date d1; return 0; }
上面的代码编译时会报错>
- 因为创建d1对象的时候要调用构造函数,这时候类里面的两个构造函数都可以不传参,这时候编译器就不知道要调用那个构造函数。
总结:
一般情况下,有内置类型成员,就要自己写构造函数,不能使用编译器自己生成的。
全部是自定义类型时,可以考虑让编译器自己生成。
❀析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
析构函数的特性
析构函数是特殊的成员变量。
特征:
- 析构函数名是在类名前面加上字符~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数,如果没有自己定义析构函数,那么系统会自动生成默认的析构函数。
【注意】:析构函数不能重载。
4.对象生命周期结束时,C++编译系统会自动调用析构函数。
typedef int DataType; class Stack { public: Stack(size_t capacity = 3) { cout << "Stack()" << endl; _array = (DataType*)malloc(sizeof(DataType) * capacity); if (NULL == _array) { perror("malloc申请空间失败!!!"); return; } _capacity = capacity; _size = 0; } void Push(DataType data) { // CheckCapacity(); _array[_size] = data; _size++; } // 析构函数 ~Stack() { cout << "~Stack()" << endl; if (_array) { free(_array); _array = NULL; _capacity = 0; _size = 0; } } private: DataType* _array; int _capacity; int _size; }; int main() { Stack s; s.Push(1); return 0; }
- 系统生成的析构函数对内置成员不做处理,自定义类型会去调用它的析构函数。
总结:
🍀小结🍀
今天我们学习了构造函数和析构函数相关的细节相信大家看完有一定的收获。