类对象数组的初始化与赋值(易错点)

类对象数组的初始化与赋值(易错点)

Apr 10, 2014
Coding
C++

下述代码中声明类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

由上可知,类数组的构造过程是自底向上的,析构过程是自顶向下的。如下图所示:

img.png