构造函数
<1>定义:用来初始化已经实例化的对象的函数,由编译器默认生成,对象实例化时会自动调用它。
<2>对不同类型的处理方式:对内置类型(int,double,指针等)不作处理,具体看编译器。对自定义类型会自动生成默认构造函数。(注意:当我们显示实现构造时,编译器就不会再自动生成默认构造了)
<3>什么时候需要显示实现默认构造呢?两种情况:1.必须需要我们手动开辟空间的情况(比如需要在堆上开空间的,像是栈,链表之类的)2.自定义类型没有默认构造的时候,比如下面这种情况
class B
{
public:
B(int n)//显示实现了B的构造,编译器将不会再生成B的默认构造
:_a(n)
{}
private:
int _a;
};
class A
{
B _b;//这种情况A就要显示实现构造,
//因为B没有默认构造可以调用,需要手动去调B的构造
};
<4>如何自己写构造:第一种用初始化列表,函数名与类名相同,没有返回值,格式--> 类名+(参数)+ “ :”+{ } ,成员变量定义在冒号后面,用逗号分隔。第二种就是在声明的位置给缺省值,其实本质也是初始化列表用这个缺省值进行初始化。一般建议只用其中一种,不要混用。(注意:初始化顺序与声明顺序相同,建议二者顺序保持一致),总结如下
下面演示显示实现一个日期类的初始化列表:
class Date
{
public:
Date(int& x, int n= 1,)//初始化列表(第一种方法)
:_a(x),
_b(n)
{
//若无其他要求函数体可为空
}
private:
int& _a;
const int _b;//可给缺省值-->const int _b=1;(第二种方法)
};
析构函数
<1>定义:用来销毁已经实例化的对象的函数,由编译器默认生成,对象生命周期结束时会自动调用它。
<2>对不同类型的处理方式:对内置类型不作处理,具体看编译器。对自定义类型会自动生成默认构造函数。不管是内置类型还是自定义类型,只要是在堆上开辟的就需要显示实现析构,在其他区(比如静态区,栈,常量区)就不用,因为只有堆上开辟的需要我们手动释放,否则会有空间泄露问题。
<3>如何自己写构造:函数名与类名相同,没有返回值,格式--> “~”+类名+(参数)+ “ :”+{ } ,以数据结构-栈为例显示实现析构:
class stack
{
public:
~Stack()//栈的析构
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
构造与析构的顺序问题
1.全局对象先于局部对象进行构造,局部对象按照出现的顺序进行构造,无论是否为static
2.类的析构函数调用一般按照构造函数调用的相反顺序进行调用。局部变量出函数就析构,但是要注意static对象的存在, 因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象,顺序在全局对象的析构之前。