• C++ 类的非静态数据成员默认初始化


    C++11 之前使用默认初始化

    C++11 之前对非静态数据成员初始化需要用到初始化列表。

    有个问题是,如果类的数据成员比较多,我们又需要定制一些数据成员的初始化操作的时候,需要写很多的构造函数。

    来看一个例子:

    #include 
    #include 
    
    class X {
    public:
        X() : a_(0), b_(0.), c_("hello world") {}
        X(int a) : a_(a), b_(0.), c_("hello world") {}
        X(double b) : a_(0), b_(b), c_("hello world") {}
        X(const std::string c) : a_(0), b_(0.), c_(c) {}
        
        void PrintX() {
            std::cout << "----------------------" << std::endl;
            std::cout << "a_: " << this->a_ << std::endl;
            std::cout << "b_: " << this->b_ << std::endl;
            std::cout << "c_: " << this->c_ << std::endl;
        }
    
    private:
        int a_;
        double b_;
        std::string c_;
    };
    
    int main() {
        X* x_1 = new X();
        x_1->PrintX();
    
        X* x_2 = new X(1);
        x_2->PrintX();
    
        X* x_3 = new X(2.0);
        x_3->PrintX();
    
        X* x_4 = new X("C++");
        x_4->PrintX();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    因为有时候要给不同的成员变量进行初始化,所以要写好几个不同版本的构造函数,并且可以发现构造函数里有很多冗余代码。

    运行结果:

    linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++11 nonstatic_datamemeber_init.cpp -o nonstatic_datamemeber_init
    linrongjian@hhb-ssd:/data/modern_cpp$ ./nonstatic_datamemeber_init
    ----------------------
    a_: 0
    b_: 0
    c_: hello world
    ----------------------
    a_: 1
    b_: 0
    c_: hello world
    ----------------------
    a_: 0
    b_: 2
    c_: hello world
    ----------------------
    a_: 0
    b_: 0
    c_: C++
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这种方式在数据成员变多的时候,需要频繁地更改和增加构造函数,维护起来非常的麻烦。

    C++11 的默认初始化方式

    C++11 允许在声明非静态数据成员的时候同时用 =(声明时默认初始化) 和 {} (列表初始化)。

    还是刚才的例子,可以这样写:

    #include 
    #include 
    
    class X {
    public:
        X() {}
        X(int a) : a_(a) {}
        X(double b) : b_(b) {}
        X(const std::string c) : c_(c) {}
        
        void PrintX() {
            std::cout << "----------------------" << std::endl;
            std::cout << "a_: " << this->a_ << std::endl;
            std::cout << "b_: " << this->b_ << std::endl;
            std::cout << "c_: " << this->c_ << std::endl;
        }
    
    private:
        // 这里也可以用列表初始化 {}
        int a_ = 0;
        double b_ = 0.;
        std::string c_ = "hello world";
    };
    
    int main() {
        X* x_1 = new X();
        x_1->PrintX();
    
        X* x_2 = new X(1);
        x_2->PrintX();
    
        X* x_3 = new X(2.0);
        x_3->PrintX();
    
        X* x_4 = new X("C++");
        x_4->PrintX();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    可以发现在声明数据成员的时候直接用 = 初始化(也可以 {} 列表初始化)和构造函数的列表初始化同时使用了。

    同时构造函数少了很多冗余代码,每个构造函数只需要专注于特殊成员的初始化(而不是写出全部数据成员的初始化),对于没写出来的数据成员,默认使用声明时初始化的值。

    显然这种方式增强了代码可维护性。

    运行效果和上面是等效的:

    linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++11 nonstatic_datamemeber_init.cpp -o nonstatic_datamemeber_init
    linrongjian@hhb-ssd:/data/modern_cpp$ ./nonstatic_datamemeber_init
    ----------------------
    a_: 0
    b_: 0
    c_: hello world
    ----------------------
    a_: 1
    b_: 0
    c_: hello world
    ----------------------
    a_: 0
    b_: 2
    c_: hello world
    ----------------------
    a_: 0
    b_: 0
    c_: C++
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    位域默认初始化(C++20)

    有些变量在存储时不需要一个完整的字节,而只需要占用几个位。

    为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。

    C++20 允许对数据成员的位域进行默认初始化。

    例:

    #include 
    
    class S {
    public:
        int x : 8 = 11;
        int y : 4{7};
    };
    
    int main() {
        S* s = new S();
        std::cout << s->x << ' ' << s->y << std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里低 8 位被初始化为 11,高 4 位被初始化为 7

    linrongjian@hhb-ssd:/data/modern_cpp$ g++ -std=c++2a bitfield_init.cpp -o bitfield_init
    linrongjian@hhb-ssd:/data/modern_cpp$ ./bitfield_init
    11 7
    
    • 1
    • 2
    • 3

    参考资料

    1. 现代C++语言核心特性解析
  • 相关阅读:
    ACN 报告 2023:国家网络安全局的一年期报告
    探索工业AI智能摄像机的高端科技
    11.10论文写作与格式
    spring cloud rebuild project z
    Llama2-Chinese项目:2.2-大语言模型词表扩充
    mysql数据库
    趣味算法一棋盘的麦子
    宝塔面板部署nginx+springboot+netty
    判断用户输入的密码是否正确,如果是123,则为正确,如果不是,就错误
    【笔记】Java - VM options、Program arguments、Environment variables、eclipse variables
  • 原文地址:https://blog.csdn.net/RJ_theMag/article/details/126810494