本篇讲述上述两种默认成员函数。
当我们创建一个空类时,里面真的什么都没有吗?不是的,任何一个类,在我们不写的情况下,都会自动生成下面6个默认成员函数
1.构造函数:主要完成类成员初始化
2.析构函数:主要完成清理资源工作
3.拷贝构造函数:用已经存在的一个对象,初始化创建另一个同类的对象
4.赋值重载函数:把一个对象赋值给另一个对象
5.取地址重载:取普通对象地址
6.取地址重载:取const对象地址
我们看下面这个类
class A1
{
public:
void set(int a = 1,double b = 0.1)
{
_a = a;
_b = b;
}
void display()
{
cout<<this->_a<<" "<<this->_b<<endl;
}
private:
int _a;
double _b;
};
int main()
{
A1 A,B;
A.set(10,10.5);
B.set(15,15.5);
A.display();
B.display();
}
我们可以通过set这个共有的成员函数给每个对象赋值,但是,如果没创建一个对象都用该函数赋值的话,就会很麻烦,如果在对象创建的时候,就直接赋值给这个对象,那么便十分简单,这里便有了构造函数。
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员 都有 一个合适的初始值,并且在对象的生命周期内只调用一次
对于上面的类我们用构造函数写出,如下:
class A1
{
public:
// void set(int a = 1,double b = 0.1)
// {
// _a = a;
// _b = b;
// }
//带参构造函数
A1(int a = 1,double b = 0.1)
{
_a = a;
_b = b;
}
void display()
{
cout<<this->_a<<" "<<this->_b<<endl;
}
private:
int _a;
double _b;
};
int main()
{
A1 A(2,2.6);//对象A中_a=2,_b=2.6
A1 B;//通过构造函数对象B中_a=1,_b=0.1
}
上面是我们显式定义的构造函数,如果不显式定义,我们也可以成功创建一个对象,并且通过类的默认构造函数为我们初始化这个对象,只不过对象成员变量为随机值。如下图:
构造函数也可以重载,即无参构造函数和带参构造函数
class A1
{
public:
A1()
{}
//带参构造函数
A1(int a = 1,double b = 0.1)
{
_a = a;
_b = b;
}
void display()
{
cout<<this->_a<<" "<<this->_b<<endl;
}
private:
int _a;
double _b;
};
int main()
{
A1 A(2,2.6);//调用带参构造
A1 B;//调用无参构造
}
这里需要注意,如果调用无参函数时,对象后面不能加(),如果加上(),就称为申明一个函数,比如还是上述类
int main()
{
A1 A(2,2.6);//调用带参构造
A1 B;//调用无参构造
//下面定义相当于申明了一个C函数,它的返回值为A1类型的对象
A1 C();
}
有人会说,类的默认构造函数,初始化的话也是随机值,还不如自己写构造函数,那么构造函数有什么好处呢?
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如 int/char…,自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现 编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数
class A1
{
public:
//带参构造函数
A1(int a = 1,double b = 0.1)
{
_a = a;
_b = b;
}
void display()
{
cout<<this->_a<<" "<<this->_b<<endl;
}
private:
int _a;
double _b;
};
class A2
{
private:
//内置类型
int _a;
double _b;
//自定义类型
A1 _t;
};
构造函数总结:
有了初始话的函数,也就会有清理资源的函数,这就有了析构函数
析构函数与构造函数类似,不过析构函数的函数名是类名前加上~,比如
class A1
{
public:
~A1()//析构函数
{}
void display()
{
cout<<this->_a<<" "<<this->_b<<endl;
}
private:
int _a;
double _b;
};
类中默认的析构函数是释放不了资源的,只有我们显式定义析构函数,才能释放堆上的资源,比如:
class A1
{
public:
A1(int* a)
{
_a = a;
_a = (int*)malloc(sizeof(int)*10);//开辟资源空间
}
~A1()//通过析构函数释放资源空间
{
if(_a)
{
free(_a);
_a = NULL;
}
}
private:
int *_a;
double _b;
};
析构函数总结: