初始化列表和构造函数内赋值的区别

1.  类的静态成员变量不能用参数初始化表初始化

(1) 如果声明了类而未定义对象,则类的一般数据成员是不占内存空间的,只有在定义对象时,在运行的时候才为对象的数据成员分配空间。但是静态数据成员不属于某一个对象,在为对象所分配的空间中不包括静态数据成员所占的空间。静态数据成员是在所有对象之外单独开辟空间,也就是在静态变量区。只要在类中指定了静态数据成员,即使不定义对象,也为静态数据成员分配空间,它可以被引用。

(2) 一般的类的成员随对象的建立而分配空间,随对象的撤销而释放。静态数据成员是在程序编译时被分配空间的,至程序结束时才释放空间。

(3) 静态数据成员可以初始化,但只能在类体外进行初始化。

也就是说,静态数据成员可以初始化,但只能在类体外初始化,并且不能用参数初始化表对其初始化。

如: class Box

     {

        public:

           int volume();

       private:

           static int height;

           int width;

           int length

}

int Box::height = 10; //正确

Box(inth, int w, int len):height(h){} //错误

 

静态成员与对象无关,属于整个类,构造函数是构造某个具体的对象。创建一个对象的时候会用到初始化表,但是静态成员在对象创建之前(在编译的时候)就已经存在了,所以不需要再初始化表中初始化。

 

2. 常数据成员只能通过构造函数初始化表对其进行初始化

    常数据成员只能通过构造函数初始化表对其进行初始化,任何其它函数都不能对常数据成员赋值。如类体中定义了常数据成员hour:

const int hour;//定义hour为常数据成员;

Time::Time(inth)

{hour= h;}  // 非法,普通赋值函数不能对之赋值

如果在类体外定义构造函数,应写成如下形式:

Time::Time(inth):hour(h){} //通过参数初始化表对常数据成员hour初始化

为什么只有初始化列表而不是普通函数能对常变量进行初始化呢?一般变量赋值有两种:一是使用初始化列表,二是在构造函数体内进行赋值操作。由于常量只能初始化不能赋值,所以常量成员必须使用初始化列表。

3、主要是性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。这部分我参考了:https://blog.csdn.net/baidu_35679960/article/details/78929018

#include
 
using namespace std;
 
class Test1
{
public:
    Test1() {cout << "Construct Test1" << endl ;} // 无参构造函数
    Test1(const Test1& t1) // 拷贝构造函数
    {
        cout << "Copy constructor for Test1" << endl ;this->a = t1.a ;
    }
    Test1& operator = (const Test1& t1) // 重载赋值运算符(也称为重载赋值函数)
    {
        cout << "assignment for Test1" << endl ;
        this->a = t1.a ;
        return *this;
    }
private:
    int a ;
};
 
class Test2
{
public:
    Test1 test1; //此处第二次调用Test 1的construct;
    //std::cout<<"进入Test2 的构造函数"<     Test2(Test1 &t1){test1 = t1 ;} //此处的“=”调用重载的“=”操作符;
};
 
int main(){
    Test1 t1; //此处第一次调用Test1的construct;
    cout<<"end of fist construct t1"<     Test2 t2(t1);
}

输出为:

Construct Test1
end of fist construct t1
Construct Test1
assignment for Test1
解释一下:
第一行输出对应调用代码中第一行,构造一个Test1对象
第三行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1 // 这就是所谓的初始化阶段
第四行输出对应Test2的赋值运算符,对test1执行赋值操作 // 这就是所谓的计算阶段

如果使用初始化列表来实现Test2的构造函数;

class Test2
{
public:
    Test1 test1 ;
    Test2(Test1 &t1):test1(t1){}
};
输出为:
Construct Test1
end of fist construct t1
Copy constructor for Test1
第一行输出对应 调用代码的第一行
第三行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。也就是说,初始化的时候调用成员列表进行初始化,就不需要用自己默认的构造函数进行初始化了。这样如果有初始化列表,那么会调用拷贝构造函数进行初始化,从而省去了重载等号进行赋值的步骤,以及调用自己构造函数进行初始化的步骤。
所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表
 

你可能感兴趣的:(C/C++)