类对象数组的初始化与赋值(易错点)
Apr 10, 2014
下述代码中声明类Example对象的数组e[2]
,然后新建了 Example(1) 对象并赋值给e[0]
。通过这种方式设置类对象数组,称为赋值,而不是初始化。
这个过程实际上赋值过程,存在临时对象Example(1)的构造和析构。而通过类对象数组初始化就不存在生成临时对象这一过程。先看类对象数组声明和赋值的代码:
#include <iostream>
using namespace std;
class Example
{
public:
Example():id(0){cout<<"Example():0"<<endl;} //无参构造构造
Example(int id):id(id){cout<<"Example():"<<id<<endl;} //有参构造函数
Example(const Example& e){this->id = e.id; cout<<"Copy Assign Constructor():"<<this->id<<endl;} // 拷贝构造函数
~Example(){cout<<"~Example():"<<this->id<<endl;}
Example& operator = (const Example& e) {
this->id = e.id;
cout<<"Copy Assign Operator():"<<e.id<<endl;
return *this;
}
int id;
};
int main()
{
// 调用默认的无参构造函数,如果不存在无参构造函数,则会编译报错。
Example e[2];
// 调用有参构造函数使用初始化方式
// Example e[2] = {Example(1),Example(2)};
cout<<"------"<<endl;
// 新建临时对象Example(1),调用copy assignment operator操作符拷贝给e[0]对象。
// Example(1)对象的生成周期只在该语句,语句结束后就会调用析构函数释放。
e[0] = Example(1);
return 0;
}
函数的输出为
Example():0
Example():0
------
Example():1
Copy Assign Operator():1
~Example():1
~Example():0
~Example():1
首先,调用无参构造函数,生成两个Example对象,所以输出两个Example():0
然后,执行Example(1) 调用有参构造函数,生成一个临时的Example类对象,所以输出Example():1
然后,将临时对象拷贝赋值给e[0],调用了赋值运算符,所以输出 Copy Assign Operator():1。该条赋值语句结束后,临时对象的生成周期就过了,调用析构函数释放内存,所以输出~Example():1。此时e[0]的id也变成了1。
最后程序结束,数组e要释放,调用e[0]和e[1]的析构函数释放内存。关于为什么先释放e[1]后释放e[0]后面会有说明。
类对象数组的初始化 #
Example e[2] = {Example(1),Example(2)};
这种方式直接初始化了数组元素为Example(1) 和 Example(2),只分别调用了一次Example(1) 和 Example(2)的构造函数,并赋值给了数组。整个过程没有生成临时对象。
类对象数组的构造和释放 #
#include <iostream>
using namespace std;
class Example
{
public:
Example():id(count){count++;} //每构造一个对象,计数值++
void print(){cout<<id<<endl;} //输出当前对象的id
~Example(){cout<<"~Example():"<<id<<endl;}
private:
int id; //类对象的id
static int count; //类对象计数。为Example类所有。
};
int Example::count = 0;
int main()
{
Example e[3];
e[0].print();
e[1].print();
e[2].print();
return 0;
}
程序输出为
0
1
2
~Example():2
~Example():1
~Example():0
由上可知,类数组的构造过程是自底向上的,析构过程是自顶向下的。如下图所示: