初始化列表效率

本文探讨了C++中初始化列表的效率问题,指出初始化列表的效率优于在函数体内的赋值操作。同时强调成员变量的初始化顺序取决于声明顺序,而非初始化列表中的顺序,对于依赖关系的初始化需额外注意。另外,文章还提到了const和static成员的初始化规则:const成员变量必须通过构造函数初始化列表完成,static成员变量需在类定义体外部初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Effective C++里有提到,看过的都知道,初始化列表效率>=在函数体里赋值。

但有一点要特别注意,成员变量初始化的顺序取决于声明的顺序,而不是在初始化列表中的顺序。vc不说啥,gcc会报warning。如果遇到初始化有依赖关系的,要特别注意。


如果你的成员是POD类型(Plain Old Data),那么list initialization和constructor initialization没有任何区别,也就是说,
struct A
{
    int a;
    double b;
    A() : a(0), b(0.0) {}
    A(int a_in, double b_in) : a(a_in), b(b_in) {}
    ~A() {}
};

// Equivalent version
struct B
{
    int a;
    double b;
    B() { a = 0; b = 0.0; }
    B(int a_in, double b_in) { a = a_in; b = b_in; }
    ~B() {}
};

以上代码是等价的。

然而,如果成员变量的类型是非POD类型,比如自定义类型,那么list initialization的代码会变成
struct Member1
{
    int val;
    // Will not compile if un-commenting the line below
    // Member1() = delete;
    
    Member1(int a) : val(a) {}
};

struct Member2
{
    double val;
    // Will not compile if un-commenting the line below
    // Member2() = delete;
    
    Member2(double b) : val(b) {}
};

struct A
{
    Member1 m1;
    Member2 m2;

    A() : m1(), m2() {}
    A(int a, double b)
    {
        m1 = a;
        m2 = b;
    }
    /* Equivalent version:
     * A(int a, double b) : m1(), m2() { m1 = a; m2 = b; }
     */
};

所以在A(int a, double b)这个构造函数里等于是做了更多的工作:先default construct m1和m2,然后再做m1和m2的copy initialization。

构造列表实际编译之后会转换成一段代码插入你写的构造函数中,是你写的代码前面哦 , 而且是按照成员变量的声明顺序进行初始化。这么说 ,你就能明白 为啥内置的类型在哪里初始化效率差的不太多,如果你自己写的类放在构造函数里面的初始化的话,会默认调用构造函数和拷贝构造函数,编译器会先于你的构造代码调用构造函数构造出这个变量,然后你写在构造函数中的估计是拷贝构造,或者是赋值构造。写在构造列表里面只会调用构造函数!效率差异由此可见!




有一个类A,其数据成员如下:
1
2
3
4
5
6
7
8
9
10
class A {
...
private:
     int a;
public:
     const int b;
     float* &c;
     static const char* d;
     static double* e;
};
则构造函数中,成员变量一定要通过初始化列表来初始化的是:______。
  • a b c
  • b c
  • b c d e
  • b c d
  • b
  • c

B
构造函数中,成员变量一定要通过初始化列表来初始化的有以下几种情况:
1、const常量成员,因为常量只能在初始化,不能赋值,所以必须放在初始化列表中;
2、引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表中;
3、没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数;

初始化从无到有,创建了新对象;如:string foo = "Hello World!"
赋值没有创建新对象,而是对已有对象赋值。 如:string bar; bar = "Hello World!"

有时我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样。
如果成员是const或者是引用的话,必须将其初始化。类似的,当成员属于某种类类型且该类没有定义默认构造函数时,也必须将这个成员初始化。(比如:类A中有一成员是B b,但类B中没有定义默认构造函数时,就必须对A中b成员进行初始化)

随着构造函数体一开始执行(即大括号里面部分),初始化就完成了(构造函数体内只是赋值操作)。因此,上面三种情况,比如初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值,


    1,const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间

    2,static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

    3,在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate = 2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static

    4,在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

         const数据成员  只在某个对象生存期内是常量,而对于整个类而言却是可变的 因为类可以创建多个对象,不同的对象其const数据成员的            值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

         const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值