1 构造函数可以分为两种:
- 普通构造函数
- 复制构造函数(又名拷贝构造函数)
2 构造函数的特性:
- 特殊的函数
- 创建对象时,自动调用(一个对象只能调用一次构造函数,只有在创建时自动调用(隐式调用)构造函数不能显示调用)
- 如果我们没有定义构造函数,系统会提供默认的构造函数(提供两个:一个普通构造, 一个复制构造),提供的普通构造函数是无参的,一旦我们自定义普通构造函数,那么这个普通构造函数就不再提供了
- 创建对象时,必须提供一个匹配的构造函数,否则无法创建对象
- 构造函数支持重载
- 构造函数支持参数默认值(如果同时存在函数重载 参数默认值,可能会出现调用的冲突)
3 构造函数的格式:
普通函数格式:返回值 函数名(形参列表);
构造函数格式:类名(形参列表);
- 构造函数名必须与类名相同
- 构造函数没有返回值类型
注:没有返回值,返回值类型为: void
没有返回值类型,连void也没有
class Point {
int x;
int y;
public:
Point() //构造函数
{
std::cout<<"无参构造函数"<<std::endl;
}
};
4 构造函数的作用
- 给对象一个标识符
- 为对象的数据成员分配空间
- 对数据成员初始化(或赋值):需要程序员自己写构造函数
class Point {
int x;
int y;
public:
Point() //构造函数
{
x = y = 0; //赋值
std::cout<<"无参构造函数"<<std::endl;
}
};
5 构造函数的注意事项
- 一个对象只能调用一次构造函数
- 通常我们都会自定义构造函数(带参),用于创建对象的初始化
- 由于我们也习惯不初始化创建对象,所以我们也要自定义无参构造
6 初始化表达式
本质:表达式
理解:用来对数据成员初始化的表达式
用来给对象的数据成员初始化的
分配空间的同时,写入数据:初始化
- 初始化表达式只能用于构造函数中
- 初始化表达式位于构造函数形参列表后,函数体前形参列表后,加:间隔
- 初始化的顺序执行顺序不是由成员变量在初始化表的顺序决定的,而是由成员变量在类中被声明时的顺序决定的(下面中的x先声明,所以x先初始化)
- 初始化表达式式中的变量顺序最好与其被声明的顺序一致
class Point {
int x;
int y;
public:
Point();
};
// Point::Point(int a, int b):初始化表达式1,初始化表达式2
//{
//}
Point::Point(int a, int b): x(a),y(b)
{
}
初始化的特殊性:
- 分配空间的同时写入数据
- 一次性写入多个数据到空间中
常见:数据的初始化
结构体的初始化 - 特殊的空间
const int a = 80;
int b = 80;
int &p = b;
创建对象的方式:
class Point {
int x;
int y;
public:
Point();
};
Point::Point()
{
std::cout<<"无参构造函数"<<std::endl;
}
int main()
{
//Point ss(); //不是创建对象, 而是函数声明
Point ss; //调用无参构造函数
Point *p = new Point;//调用无参构造函数
Point *q = new Point();//调用无参构造函数,和new Point等价
}
7 复制构造函数
本质:构造函数
理解:做复制操作的构造函数
如果我们没有自定复制构造函数,系统会提供一个默认的
当创建对象时,将一个同类型的对象整体赋值时,就会调用复制构造:将数据成员一一对应的复制过去
复制构造函数:
参数: 一个,通常是该类的常引用
class Point
{
int x;
int y;
public:
Point(){}
Point(const Point &n)
{
std::cout<<"复制构造"<<std::endl;
this->x = n.x;
this->y = n.y;
}
};
int main()
{
Point a;
Point b(a); //调用复制构造函数
Point c = a; //调用复制构造函数
Point d;
d = a; //不是复制构造,初始化的时候已经调用了普通构造,这里是赋值运算符重载函数
}
构造函可以有很多,但是复制构造函数只有一个
注意:自定义了复制构造函数之后,系统就不会提供默认的普通构造函数,所以自定了复制构造函数就必须自定义普通构造函数
什么时候需要自定复制构造:
- 通常只要类的内部不出现堆区空间是,就使用默认的复制构造就行了;
- 如果类的内部出现成员指向堆区空间,就必须自定义了
class Point
{
int x;
int y;
char* name = NULL;
public:
Point(){}
Point(int a, const char *n){
int len = strlen(n) + 1;
name = new char[len];
strcpy_s(name, len, n);
}
Point(const Point &n)
{
std::cout<<"复制构造"<<std::endl;
this->x = n.x;
this->y = n.y;
int len = strlen(n.name) + 1;
this->name = new char[len];
strcpy_s(this->name, len, n.name);
}
~Point()
{
if(name)
{
delete name;
name =NULL;
}
}
};
int main()
{
Point a(1, "hehe");
Point b(a);
}
8 总结
- 通常普通构造函数都会自定义:1个无参 1个带参
- 通常析构函数、复制构造函数可以不写(使用默认的)
- 当类内部出现指针成员指向堆区空间,存在内部分配堆区空间,通常就需要自定义析构和复制构造了