如不清楚类的定义可以点击此篇文章:类的定义与引入
C++为很么要引入构造函数和析构函数呢,前文讲到大佬引入了类这个概念,但是大佬用起来感觉不太方便用,虽然不会出现有人不按接口调用而导致出问题了,但是我每次使用栈的时候依然要进行初始化和销毁(资源释放),而且经常会忘记调用,于是便引入了构造函数和析构函数,来解决这个问题。
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
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 mian()
{
Date d1; // 调用无参构造函数
Date d2 (2015, 1, 1); // 调用带参的构造函数
// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
Date d3();
return 0;
}
class Date
{
public :
private :
int _year ;
int _month ;
int _day ;
};
int main()
{
Date d1;
return 0;
}
既然不定义构造函数编译器会自动生成,那我们试一下定义一个变量d1监视看一下,编译器有没有帮我们初始化。如下图
这里可以看到d1中数据为随机值,编译器并没有帮我们处理。
有的同学可能会想编译器生成的默认构造函数好像没啥用啊?
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如int/char…,自定义类型就是我们使class/struct/union自己定义的类型,C++默认生成的构造函数对内置类型不作处理,对自定义类型会去调用他的构造函数。
例如使用栈的时候就不用自己写构造函数,编译器默认生成的就可以。
如两个栈实现一个队列:
class MyQueue
{
public:
void push(int x)
{
}
private:
Stack PushST; //这里就可以直接定义,编译器会自动调用他的构造函数
Stack PopST;
};
像我们第一段程序中带参构造函数调用之后,虽然对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化,构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
例如:
class Date
{
public :
//初始化列表 -- 成员变量定义的地方
Date (int year, int month , int day )
:_year(year)
,_mouth(mouth)
{
_day = day; //也可以在函数体内定义
}
private :
int _year ; //声明
int _month ;
int _day ;
};
class A {
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B {
public:
B(int a, int qq)
:_aa(a)
,_qq(qq)
,_n(10)
{
//若是放在此处定义程序则会报错
//aa(a);
}
private:
A _aa; // 没有默认构造函数
int& _qq; // 引用
const int _n; // const
};
class A {
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print()
{
cout<<_a1<<" "<<_a2<<endl;
}
private:
int _a2;
int _a1;
}
int main()
{
A aa(1);
aa.Print();
}
根据第四点我们可以知道初始化列表会先初始化_a2,此时_a1并没有初始化故_a2为随机值,然后初始化_a1故_a1 = 1。
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
析构函数是特殊的成员函数。
其特征如下:
定义: 以顺序表为例
typedef int DataType;
class SeqList
{
public:
SeqList(int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList()
{
if (_pData)
{
free(_pData); // 释放空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
}
}
private:
int* _pData;
size_t _size;
size_t _capacity;
};
int main()
{
SeqList SL1;
SeqList SL2;
}
析构函数是在对象生命周期结束时调用,例如这里会先调用SL2的析构函数然后调用SL1的析构函数。
总结:
我们不写,编译器默认生成的构造函数和析构函数
1.对内置类型变量不处理,对自定义类型变量会去调用他们的构造函数和析构函数。
2.构造函数和析构函数需要根据实际情况来使用,例如像上述说的日期类,我们就需要自己实现构造函数,而栈就不用自己实现,编译器会自己去调栈的构造函数。同样日期类也不用去实现他的析构函数,因为没有资源需要进行释放,而栈类则需要自己实现析构函数,因为需要释放资源;
下面一片文章建议一起学习
拷贝构造与运算符重载