构造函数
成员函数的一种,名字与类名相同,可以有参数,不能有返回值
作用是对对象进行初始化,如给成员变量赋值
如果定义类时没写构造函数,则编译器会生成一个默认的无参数且不做任何操作的构造函数;而当定义时写了构造函数,那么默认的构造函数不会生成,如果定义的构造函数有参数,定义类的对象的时候一定要带着参数定义,例如:
class Complex {
private:
double real, imag;
public:
Complex(double r, double i = 0);
};
Complex::Complex(double r, double i) {
real = r, imag = i;
}
Complex c1; //error,缺少构造函数的参数
Complex c1(2); // OK
Complex c1(2, 3), c2(3, 5); // OK
Complex * pc = new Complex(3, 4); // OK
一个类可以有多个构造函数,参数个数或类型不同,它们形成重载关系
C++11列表初始化可以用于类,只要提供与某个构造函数的参数列表匹配的内容
class CSample {
int x;
public:
CSample() {
cout << "1" << endl;
}
CSample(int n) {
x = n;
cout << "2" << endl;
}
};
int main() {
CSample array[2] = {3};
/*
array[0]提供了3可用于构造函数, 输出2;
array[1]未提供构造函数的参数, 故调用第一个构造函数, 输出1.
*/
return 0;
}
复制构造函数
只有一个参数,即对同类对象的用
形如 X::X( X& ) 或 X::X( const X & ) ,后者能以常量对象作为参数
如果没有定义复制构造函数,那么编译器会生成默认复制构造函数,默认复制构造函数完成复制功能,一个类有且仅有一个复制构造函数
class Complex {
private:
double real, imag;
};
Complex c1; // 调用缺省无参构造函数
Complex c2(c1); // 调用缺省的复制构造函数, 将c2初始化成和c1一样
复制构造函数必须是传递引用,因为如果定义一个按值复制构造函数,那么编译器必须在调用的时候创建该参数的副本,这意味着必须再次调用复制构造函数,这样就会进入一个无限递归地调用过程,最终导致栈溢出
复制构造函数起作用的情况:
1):当用一个对象去初始化同类的另一个对象时(定义时的“=”为初始化语句而非赋值语句)
2):如果一个函数有一个参数是类A的对象,那么该函数被调用的时,类A的复制构造函数将被调用
3):如果一个函数的返回值是类A的对象,则函数返回时,A的复制构造函数被调用
注意:对象间的赋值不导致复制构造函数被调用(因为目标对象已存在,不需要通过构造函数创建新对象,注意复制构造函数本身为“构造函数”)
类型转换构造函数
定义类型转换构造函数的目的是实现类型的自动转换
只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数;当需要的时候,编译系统会自动的调用转换构造函数,建立一个无名的临时对象
class Complex {
public:
double real, imag;
Complex(int i) { // 类型转换构造函数
real = i, imag = 0;
}
Complex(double r, double i) { real = r, imag = i; }
};
int main() {
Complex c1(7, 8);
Complex c2 = 12;
c1 = 9;
return 0;
}
析构函数
名字与类名相同,在前面加‘~’,没有参数和返回值,一个类最多只能有一个析构函数
析构函数在对象消亡时自动被调用,可用来做释放分配的空间等“善后工作”;如果没有定义类时未写析构函数,则编译器会生成缺省析构函数,缺省析构函数什么也不做
class String {
private:
char * p;
public:
String() {
p = new char[10];
}
~String();
};
String::~String() {
delete [] p;
}
注意1:一个局部变量的生存期是从定义开始,到包括了它的最内层的"}"为止
注意2:程序结束时,先初始化的后析构