乐趣区

关于c++:带你掌握C中三种类成员初始化方式

摘要: 在 C ++11 之后,申明时初始化 -> 初始化列表 -> 构造函数初始化。

本文分享自华为云社区《如何编写高效、优雅、可信代码系列(3)——类成员初始化的三种形式》,原文作者:我是一颗大西瓜。

首先,先得理解一下 C ++ 反对哪几种类成员初始化的形式,你罕用的又是哪一种。

  • 初始化形式一:初始化列表
class A
{
public:
    int a; // 初始化列表
    A(int a_):a(a_){}};
  • 初始化形式二:构造函数初始化
class A
{
public:
    int a; // 初始化列表
    A(int a_, bool b) {a = a_;}
};
  • 初始化形式三:申明时初始化(也称就地初始化,c++11 后反对)
class A
{
public:
    int a = 1; // 申明时初始化
    A() {}
};

在 C ++98 中,反对了在类申明中应用等号“=”加初始值的形式,来初始化类中动态成员常量。这种申明形式咱们也称之为“就地”申明。就地申明在代码编写时十分便当,不过 C ++98 对类中就地申明的要求却十分高。如果动态成员不满足常量性,则不能够就地申明,而且即便常量的动态成员也只能是整型或者枚举型能力就地初始化。而非动态成员变量的初始化则必须在构造函数中进行。比方,如下代码在 c ++98 中编译

class Init
{
public:
    Init(): a(0) []
    Init(int d): a(d) {}
private:
    int a;
    const static int b = 0;

    int c = 1;           // member, cannot pass build
    static int d = 0;    // member, cannot pass build

    static const double e = 1.3;      // not int or enum type, cannot pass build
    stati const char* const f = "e";  // not int or enum type, cannot pass build
}

这十分不不便,所以在 C ++11 中,规范容许非动态成员变量的初始化有多种形式。具体而言,除了初始化列表外,在 C ++11 中,规范还容许应用等号 = 或者 花括号 {} 进行就地的非动态成员变量初始化。

struct init {
    int a = 1;
    double b {1.2};
};

大家晓得,有几种状况下举荐优先应用列表初始化

  • const 成员变量只能用成员初始化列表来实现初始化,而不能在构造函数内赋值
  • 初始化的数据成员是对象
  • 须要初始化援用成员数据

具体的起因这里不细述,大家能够去看一下《C++ Primer》。

构造函数初始化的实质是赋值操作(”=”),这个办法存在两个问题,一个是比起初始化列表和就地初始化,此形式的效率偏低;第二个是可能存在谬误隐患。

先说第一个,赋值过程中会产生长期对象,长期对象的结构析构会造成效率损耗,初始化列表的形式就防止了产生长期对象缩带来的问题。

第二个是,如果你没有重写或者禁止赋值构造函数,c++ 会悄悄的加上默认的赋值构造函数,这个时候也有可能带来问题。

从 C ++11 之后,这三种初始化的办法都能够应用,并不会存在抵触,然而,他们之间是有优先级程序的,这个优先级来源于他们在初始化的工夫程序,前面初始化的会把后面的笼罩掉,成员变量的初始化程序是

申明时初始化 -> 初始化列表 -> 构造函数初始化

因而如果三种初始化形式同时存在的话,那么最初保留的成员变量值必定是构造函数中初始化的值。

#include <iostream>
using namespace std;
class A 
{
public:
    int a = 1;
    A(int a_) :a(2) {a = 3;}
};

int main()
{
    A a;
    cout << "a.a=" << a.a << endl;
    return 0;
}

// a.a=3

既然初始化形式这么多,那么什么时候实用哪种呢?

1. 申明时初始化的应用场景

  • 一个长处是直观,你在申明的时候顺便给一个初始值,bravo,他人在看你代码的时候,点一下调到申明也能看到你赋予的初始值,不必再去看构造函数那里给的什么值
  • 第二个长处更有用了,比方你要定义多个构造函数,每个构造函数都用列表初始化的办法初始化,多麻烦呀,请看上面的例子,妈妈看了再也不必放心我想用其余初始化办法了
class Group {
public:
    Group() {}
    Group(int a): data(a) {}
    Group(Mem m): mem(m) {}
    Group(int a, Mem m, string n): data(a), mem(m), name(n) {}
private:
    int data = 1;
    Mem mem{0};
    string name{"Group"};
};

2. 列表初始化的应用场景

后面说过了三个场景,这里赘述一下

  • const 成员变量只能用成员初始化列表来实现初始化,而不能在构造函数内赋值
  • 初始化的数据成员是对象
  • 须要初始化援用成员数据

然而,须要留神列表初始化的程序,不过 IDE 会提醒你的

3. 构造函数初始化的应用场景

  • 第一个就是拷贝和赋值构造函数里(不然怎么叫赋值构造函数呢)
  • 第二个就是比拟无聊的状况了,比方你想把几个成员函数都初始化成一个值,请看上面例子
class Group {
public:
    Group() {data1 = data2 = data3 = 0;}
private:
    int data1;
    int data2;
    int data3;
};

一言以蔽之,优先就地初始化和列表初始化。

点击关注,第一工夫理解华为云陈腐技术~

退出移动版