C++:构造函数与析构函数
构造函数
类的对象都是由构造函数产生的
同 Java 一样,若类未定义构造函数,则会产生默认构造函数(无参构造函数)
class complex{
private:
double real, image;
public:
complex(){}
complex(double r){ real = r; image = 0; }
complex(double r, double i){ }
complex(complex c1, complex c2){
real = c1.real + c2.real;
image = c1.image + c2.image;
}
};
构造函数可以使用初始化列表简写
complex (double r, double i) : real(r), image(i){}//用r初始化real,用i初始化image
构造函数的调用
complex c1;//complex()
complex c2();//complex()
complex c3(5);//complex(5)
complex *c4p = new complex(5, 6);//complex(5, 6)
complex cs1[3] = {1, complex(3,2) };
//cs1[0] -> complex(1) cs1[1] -> complex(3, 2) cs1[2] -> complex()
complex *csp1[2] = new complex[2];//均调用无参构造函数
complex *csp2[2] = {};//未创建任何对象
complex *csp3[3] = {new complex(1), new complex()};
//指针数组,前两个元素使用complex(1)、complex(),最后一个指针未初始化,仅创建了两个对象
复制构造函数
一个类一定有且仅有一个复制构造函数
形如 X::X(X &x)
或 X::X(const X &x)
,参数必须是引用类型
若未自定义复制构造函数,编译器自动添加默认的构造函数
构造函数的作用类似 Java 中 Object.clone()
作用场景:
- 用一个对象去初始化同类的另一个对象时
Complex c2(c1);
或
Complex c2 = c1;
(因为是声明语句,所以调用了复制构造函数) - 如果某个函数的参数为类对象,该函数调用时将使用复制构造函数初始化该参数
- 当函数的返回值是一个类的对象时,返回的对象是通过以函数返回的对象作为参数
调用复制构造函数产生的(无法返回一个局部变量)
对象间的赋值,并不会调用复制构造函数
此外,为避免调用复制构造函数初始化形参的开销,在合适的时候使用引用及引用常量类型的参数
转换构造函数
只有一个参数且不是复制构造函数的构造函数,一般可视作转换构造函数
在需要的时候,编译系统会自动调用转换构造函数,制造一个无名的临时对象
临时对象就是不可见的匿名对象,也叫无名对象,没有名字故无法继续使用
仅在运行中需要时生成,使用后析构
临时对象在 “full expression” 结束时被析构,即 生成临时对象的最大表达式结束处即是临时对象的析构处
complex::complex(double r){ real = r; image = 0;}//double型转换为complex类型
complex c = 5;//complex(5)
c = 9;//调用complex(9)
complex c2(3);//complex(3)
转换构造函数的作用是实现类型的转换,分为隐式转换和显式转换
上面 c = 9
即为隐式转换
使用 explicit 关键字表明禁止其修饰的构造函数进行隐式的类型转换
c = complex(9);//函数仍然可以显式调用
隐式转换常常会导致迷惑行为,需要小心使用
string str = 'a';//实际上调用的是 string(int size), 可能误以为调用了string(char c)等
析构函数
名字与类名相同,在函数名前加 ~
,一个类最多只有一个析构函数
编译器默认生成空实现的析构函数
每个对象消亡前自动调用析构函数,在对象结束生命周期前做善后工作,如释放资源
使用 delete 时、数组元素、局部对象消亡时也会调用析构函数,
class String{
char *p;
public:
complex(size_t size){ p = new char[size]; }
~ complex(){ delete [] p; }
}
构造函数与析构函数调用示例
封闭类
成员变量中有类对象时,该类称为封闭类(enclosing)
对于封闭类,默认构造函数不可用,因为没有说明成员对象如何构造
因此必须使用有初始化列表说明成员对象如何构造的构造函数
封闭类的构造函数调用时,成员对象按照类的声明顺序
按照初始化列表调用相应的构造函数,或者使用默认构造函数 依次构造
封闭类的析构函数调用后,会依次调用成员对象的析构函数,次序与构造函数相反
当使用封闭类的默认复制构造函数时,其默认实现使用复制构造函数初始化成员对象
当然,如果自定义复制构造函数,可以使用初始化列表指定成员对象的构造方式
2020/1/17