默认成员函数是指在一个什么成员都没有的空类中,编译器会自动生成的成员函数叫做默认成员函数。类的6个默认成员函数为:1、构造函数(进行初始化)2、析构函数(进行清理)3、拷贝构造函数(同类对象初始化创建对象)4、赋值重载函数(一个现存对象的值拷贝给另一个现存对象)5、普通对象取地址(很少自己实现)6、const 对象取地址(很少自己实现)
1、构造函数
构造函数是一个特殊的成员函数,构造函数的名字与类名相同,主要用于初始化成员属性。在创建对象时由编译器自动调用构造函数,以保证每个成员属性都有一个合适的初始值。并且在对象的整个生命周期内只调用一次。
构造函数的特性:
1、函数名与类名相同。
2、无返回值(不需要写void)。
3、对象实例化时编译器自动调用对应的构造函数。
4、构造函数可以重载。
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;
};
void TestDate()
{
Date d1; // 调用无参构造函数
Date d2(2015, 1, 1); // 调用带参的构造函数
//Date d3();错误写法,跟函数声明区分不开
}
如果要进行无参调用,直接对类进行实例化即可,这里切记不能在无参调用这里后面跟上括号,Date d3()是错误写法。
如果用户显示定义了构造函数,那么编译器将不再自动生成。如果用户没有显示定义构造函数,编译器自动生成的构造函数不会对内置类型成员属性进行初始化,对于自定义类型的成员属性,编译器会自动调用该自定义类型的构造函数进行初始化。
为了解决编译器自动生成的构造函数对内置类型成员属性不进行初始化的问题,在C++11中打上了补丁,即:内置成员属性在类中声明时可以给缺省值(用于编译自动生成的构造函数对内置成员属性进行初始化)
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)可设置缺省值
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
默认构造函数 :无参的构造函数和全缺省的构造函数都是默认构造函数,并且默认构造函数有且只能存在一个,全部存在时无参调用会发生调用冲突。注意:无参构造函数、全缺省的构造函数、我们没写编译器自动生成的构造函数,都可以认为是默认构造函数。
结论:1、一般情况下,构造函数都需要自己写。
2、下面两种情况下可以让编译器自动生成:
2.1内置类型成员属性有缺省值,且符合我们的要求。
2.2成员属性类型均是自定义类型,且自定义类类型有自己的构造函数。
2、析构函数
析构函数与构造函数的功能相反,析构函数不是完成对对象本身的销毁,局部对象的销毁是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
析构函数的特性:
1、析构函数名是在类名前加上字符~。
2、无参数且无返回类型。(不需要写void)
3、析构函数不能重载,一个对象只能存在一个析构函数。
4、对象生命周期结束时由编译器自动调用。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_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()//析构函数
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
}
如果用户没有显示定义析构函数,编译器自动生成的析构函对于内置类型不做处理,对于自定义类型会去调用自定义类型本身的析构函数。
结论:1、有动态内存申请,就需要显示写析构函数,进行内存的释放。
2、下面两种情况可以不用用户自己写析构函数:
2.1、成员属性不涉及动态内存申请。
2.2、涉及动态内存申请的成员类型 为自定义类型,且有自己的析构函数。