🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解C++中有关类和对象的介绍,本篇是中篇的第一篇文章.
金句分享:
✨别在最好的年纪,辜负了最好的自己.✨
目录
- 一、"构造函数"与"析构函数"
- 1.1 "构造函数"
- (1) 自动生成的"构造函数"
- (2) 自定义"构造函数"
- 总结:
- 1.2 "析构函数"
- 总结:
一、“构造函数"与"析构函数”
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
常见的三种默认构造函数:
- 无参构造函数
- 全缺省构造函数
- 带参构造函数
当然,学过函数重载的友友 们一定知道全缺省和无参会存在调用不明确的情况.
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;
};
运行结果:
总结:
- 构造函数的函数名与类名相同。无返回值(不是void)。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。(为了解决满足多样的初始化要求)
- 默认构造函数是对
内置类型
不进行处理,C++11
中,打了个补丁,内置类型
成员变量在类中声明时可以给默认值(缺省值) - 如果类中没有显式定义构造函数,则
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
我们不难发现,即使我们不写函数调用,构造函数和析构函数都会被自动调用.不愧是祖师爷的亲儿子,两个函数都有特权.
总结:
- 析构函数名是类名前加上字符 ~。无参数无返回值类型。
- 与构造函数不同的是,析构函数不支持函数重载,所以一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象生命周期结束时,
C++
编译系统系统自动调用析构函数。 - 编译器生成的默认析构函数对自定义类型会调用它自己的析构函数,析构函数对于内置类型,不会有任何影响,因为内置类型的对象没有需要释放的资源。
- 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
Date
类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack
类。但是呢,牛牛建议还是都写吧,否则忘记写就可能发生内存泄漏,反正不难.
本篇文章只提到了"构造"与"析构"函数这两个天选之子,还有四个没有登场哦!敬请期待吧!