一、“构造函数"与"析构函数”
1.1 “构造函数”
不知道友友们有没有过这样一段经历.
在写一道数据结构的oj题时,信心满满的提交后,发现,编译居然编译不过,找了半天发现是忘记了进行初始化操作.
很多时候我们经常忘记初始化操作,但是初始化操作每次又是必做的,那么C++的祖师爷(本贾尼大佬)就贴心的给我设计了一个函数,这个函数就是构造函数.
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
(1) 自动生成的"构造函数"
构造函数编译器会自动调用,那我们不写构造函数会怎样呢?
下面这段代码会报错吗?
#include <iostream> using std::cin; using std::cout; using std::endl; class Date { public: void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Print(); Date d2; d2.Print(); return 0; }
答案:
并不会报错,因为构造函数即使如果在类中没有显式定义,编译器也会自动生成一个默认的析构函数
运行结果:
-858993460–858993460–858993460 -858993460–858993460–858993460
但是.构造函数对于内置类型(例如:int ,double,指针等等)并不做处理,对于自定义类型(结构体,类,联合,枚举等等),会调用自定义类型自己的构造函数.
#include <iostream> using std::cin; using std::cout; using std::endl; class Time { public: Time()//Time类的默认构造函数 { cout << "Time的构造函数" << endl; _hour = 0; _minute = 0; _second = 0; } void Print() { cout << _hour << "-" << _minute << "-" << _second << endl; } private: int _hour; int _minute; int _second; }; class Date { public: void Print() { cout << _year << "-" << _month << "-" << _day << endl; _time.Print();//打印时间 } private: int _year; int _month; int _day; Time _time; }; int main() { Date d1; d1.Print(); Date d2; d2.Print(); return 0; }
运行结果:
Time的构造函数 -858993460–858993460–858993460 0-0-0 Time的构造函数 -858993460–858993460–858993460 0-0-0
解释:
虽然Date类我们没有写构造函数,但是编译器自动生成了一个隐藏的构造函数并且对自定义类型(这里是Time类)会调用自己的构造函数,所以Time类中的成员已经初始化了.
(2) 自定义"构造函数"
前面提到,内置类型,默认构造函数是不进行处理的,而这显然是设计的不合理的,所以在C++11中,打了个补丁,内置类型成员变量在类中声明时可以给默认值(缺省值),记住这里是默认值,并不是真的存储变量,因为声明是没有空间存储变量的.类只是图纸,并不能住人,只有实例化成对象后,才可以住人.
示例:下面这段代码并没有显示定义构造函数.
#include <iostream> using std::cin; using std::cout; using std::endl; class Date { public: void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year=2020;//类中只是声明,并不存储变量,这里给出的是缺省值(很重要) int _month=10; int _day=1; }; int main() { Date d1; d1.Print(); printf("\n"); Date d2; d2.Print(); return 0; }
运行结果:
2020-10-1 2020-10-1
常见的三种默认构造函数:
1.无参构造函数
2.全缺省构造函数
3.带参构造函数
当然,学过函数重载的友友 们一定知道全缺省和无参会存在调用不明确的情况.
class Date { public: Date()//无参构造函数 { _year = 2020; _month = 1; _day = 1; } Date(int year=2020,int month=1,int day=1)//全缺省构造函数 { _year = year; _month = month; _day = day; } Date(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; };
运行结果:
总结:
1.构造函数的函数名与类名相同。无返回值(不是void)。
2.对象实例化时编译器自动调用对应的构造函数。
3.构造函数可以重载。(为了解决满足多样的初始化要求)
4.默认构造函数是对内置类型不进行处理,C++11中,打了个补丁,内置类型成员变量在类中声明时可以给默认值(缺省值)
5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成,所以无参,全缺省,默认构造函数只能有一个.
1.2 “析构函数”
同样,我们设计构造函数的目的是帮助我们自动调用初始化操作,因为初始化操作经常忘记且又是必须的.那析构函数呢?
析构函数是很多时候我们经常忘记销毁操作,如果是动态申请的空间,很容易导致内存泄漏,那么我们亲爱的C++的祖师爷(本贾尼大佬)就又给我们设计了一个函数,这个函数就是析构函数.
析构函数:
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
构造函数名与类名相同,那析构函数呢?
析构函数名是在类名前加上字符 ~。
示例:
#include <iostream> using std::cin; using std::cout; using std::endl; typedef int DataType; class Stack { public: Stack(int capacity=5)//全缺省构造函数 { 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: void CheckCapacity() { if (_size == _capacity) { int newcapacity = _capacity * 2; DataType* temp = (DataType*)realloc(_array, newcapacity * sizeof(DataType)); if (temp == NULL) { perror("realloc申请空间失败!!!"); return; } _array = temp; _capacity = newcapacity; } } private: DataType* _array; int _capacity; int _size; }; int main() { Stack s; s.Push(1); s.Push(2); s.Push(3); s.Push(4); return 0; }
运行结果:
Stack ~Stack
我们不难发现,即使我们不写函数调用,构造函数和析构函数都会被自动调用.不愧是祖师爷的亲儿子,两个函数都有特权.
总结:
1.析构函数名是类名前加上字符 ~。无参数无返回值类型。
2.与构造函数不同的是,析构函数不支持函数重载,所以一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
3.对象生命周期结束时,C++编译系统系统自动调用析构函数。
4.编译器生成的默认析构函数对自定义类型会调用它自己的析构函数,析构函数对于内置类型,不会有任何影响,因为内置类型的对象没有需要释放的资源。
5.如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。但是呢,牛牛建议还是都写吧,否则忘记写就可能发生内存泄漏,反正不难.
本篇文章只提到了"构造"与"析构"函数这两个天选之子,还有四个没有登场哦!敬请期待吧!