C++提供了构造函数来处理对象的初始化。
构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。
构造函数的名字与类名同名,以便编译系统识别;不具有任何类型,不返回任何值。
也可以在类内对构造函数进行声明,而在类外定义。
如果用户没有定义构造函数,则C++系统会自动生成一个构造函数,只是这个构造函数的函数体是空的,也没有参数。
这个函数叫做默认构造函数
如果定义类的时候不给出构造函数和析构函数,那么C++编译器会自动生成一个无参构造函数、copy构造函数和一个析构函数。
如果定义了构造函数,那么C++编译器就不会生成无参构造函数,仍然会生成copy构造函数。
C++编译器提供的copy构造函数执行的是浅copy,如果需要执行深copy,需要自定义copy构造函数:
#include <iostream>
using namespace std;
#pragma warning (disable:4996)
class teacher {
public:
teacher(int age, char *name) {
this->age = age;
this->name = (char *)malloc(20);
strcpy(this->name, name);
}
~teacher() {
free(this->name);
}
public:
void print() {
cout << age << endl;
cout << name << endl;
}
private:
char *name;
int age;
};
void display_run(){// 显示对象的生命周期
char s[] = "tom";
teacher a(20, s);
teacher b = a;
a.print();
}
int main() {
display_run();
system("pause");
}
注:teacher b=a; 执行的是C++编译器提供的copy构造函数,只是复制指针变量的值,并不复制指针变量所指的内存空间,所以,b对象在free的时候会出现coredump。
此时,需要自定义copy构造函数,执行深copy。
有参数的构造函数,实参是在定义对象时给出的。
C++还提供另一种初始化数据成员的方法-参数初始化表 来实现对数据成员的初始化。
在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化方法,这些构造函数具有相同的名字,而参数不同,这称为构造函数的重载。
构造函数初始化:
- 类名 对象(实参1,实参2....) //point p1(1,2);
- 参数初始化表 // void get_cube(int a,int b,int c):width(a),length(b),height(c){}
如果在一个类中定义全部默认参数的构造函数,不能再定义重载构造函数。//会产生二义性
C++编译器不能为某些类生成默认构造函数 (这个类包含一个其他类类型的成员且这个成员没有默认构造函数)
对象在以下情况下会发生拷贝:
- 初始化对象时 //调用构造函数
- 以值的方式传递参数 //实参传递对象给形参
- 返回一个对象 //函数返回
#include <iostream>
using namespace std;
class point {
public:
point(int x, int y) {
this->x = x;
this->y = y;
}
point(const point &p) {
this->x = p.x;
this->y = p.y;
}
~point() {
cout << "析构" << endl;
}
public:
void getP() {
cout << x;
}
private:
int x;
int y;
};
void test(point p) {
p.getP();
}
point test1() {
point p1(4, 5);
return p1;
}
int main() {
point p(1, 2);
point p1 = p; //第一种调用时机
point p2(p); //第一种调用时机
test(p); //第二种调用时机
point p3 = test1(); //第三种调用时机 返回一个匿名对象,匿名对象转成p3
point p4(1, 2);
p4 = test1(); //第三种调用时机 返回一个匿名对象,匿名对象复制到p4,匿名对象被析构
system("pause");
}
构造函数的初始化列表使用时机:
- 数据成员是const
- 数据成员是引用
- B类包含A类对象,并且A类不存在默认构造函数
#include <iostream>
using namespace std;
class A {
public:
A(int a) {
this->a = a;
}
private:
int a;
};
class B {
public:
B(int b,int d):a(d) {
this->b = b;
}
private:
int b;
A a;
};
int main() {
B b1(1, 2);
system("pause");
}
B类组合A类对象,先调用A类的构造函数,再调用B类的构造函数。析构的顺序与构造顺序相反。
如果有多个组合对象,调用构造函数的顺序按照定义顺序而不是根据初始化列表的顺序。