本文总结了类的创建、复制和销毁过程中涉及到的成员函数:构造函数、析构函数、拷贝构造函数、赋值运算符等。 探讨了全局对象、静态对象、栈中的对象、堆中的对象的整个生命周期中,这些成员函数的调用时机。
类的声明
C++ 引入类的概念来实现面向对象程序设计,先来看一个简单的类的声明:
class CPerson{
public:
// 构造函数
CPerson(){
cout<<"constructor"<<endl;
}
// 构造函数(重载)
CPerson(int age){
cout<<"constructor "<<age<<endl;
}
// 声明可以与实现分离
void Hello();
};
CPerson::Hello(){
cout<<"hello"<<endl;
};
- 构造函数定义为私有,可以防止该类被直接实例化。一般用于Singleton实现。
- 不允许签名为CPerson(CPerson p){}的构造函数(编译错)。不允许带参数的析构函数(编译错)。构造函数可以有参数,不允许有返回值。总之,析构函数和构造函数签名不正确都会编译错。
对象实例化
可以直接定义对象变量,在栈中分配并初始化对象;也可以定义对象指针,从堆中分配空间并初始化对象。
CPerson p1;
CPerson p2(2);
CPerson* p3 = new CPerson(3);
输出:
constructor
constructor 2
constructor 3
对象数组
CPerson *p[3] = {new CPerson(1), new CPerson(2)};
输出:
constructor 1
constructor 2
对象指针CPerson *与int并非兼容的类型,因而需显式地指定指针类型的表达式(new CPerson(1))。 同时,未指定项的指针值为空。
复制构造函数
类的声明中,签名为CPerson(CPerson&)的方法称为复制构造函数,用来从一个已存在的对象复制生成一个新的对象。 在如下三种情况下会被调用:
- 用一个对象初始化另一个对象时。例如:
CPerson p2(p1);
CPerson p2 = p1;
- 对象作为参数传递时。例如:
void func(A a){}
- 对象作为返回值时。例如:
A func(){ A a; return a;}
只有一个参数的复制构造函数可以被称为转换构造函数。当需要类型转换时,会被调用:
CPerson = 2; // CPerson(int) called
注意区分赋值和初始化:对象变量间赋值不会调用复制构造函数。赋值只会按位拷贝对象所在的内存。当然你也可以重载operator=来改变它的行为。
析构函数
类的声明中,签名为~CPerson()的方法称为析构函数。析构函数没有参数和返回值。当对象生命周期结束时被调用,通常用来释放资源。
一个类只能由一个析构函数。析构函数与构造函数类似,用户不指定时编译器会生成一个缺省的析构(构造)函数, 缺省的析构(构造)函数是空函数。
对象生命周期
如下程序解释了对象的声明周期何时开始,以及何时结束。涉及到了:全局对象、静态对象、栈中的对象、堆中的对象。
CPerson p1; // main执行前,构造函数被调用
void func(){
static CPerson p2; // func第一次执行前,构造函数被调用
CPerson p3; // p3的构造函数被调用
// func结束时,p3的析构函数被调用
}
int main(){
CPerson p4, *p5; // 调用p4的构造函数
func();
p5 = new CPerson; // 调用p5的构造函数
delete p5; // 调用p5的析构函数
// main结束时,p4的析构函数被调用
}
// 程序结束前,p1, p2的析构函数被调用
版权声明
参考代码
要获取本文的参考代码,请访问: www.tianmaying.com/tutorial/cp…