我们之前在 C 语言中可以定义 const 成员,那么我们是否可以在类中定义 const 成员呢?我们来看看下面代码中的类定义是否合法呢?如果合法,ci 的值是什么,存储在哪里呢?

#include <stdio.h>

class Test
{
private:
    const int ci;
public:
    int getCI()
    {
        return ci;
    }
};

int main()
{
    Test t;
    
    printf("t.ci = %d\n", t.getCI());
    
    return 0;
}

        照我们之前学习的知识可以猜测对象 t 是创建于栈上,那么它中的 ci 便会是随机值了。我们看看编译结果

初始化列表的使用(十五)_列表

        编译报错,它说我们没有初始化 ci,下面我们在它定义的时候并初始化为10。也就是将第 6 行改为 const int ci = 10;我们再次看看它是否编译通过初始化列表的使用(十五)_初始化_02

        我们看到编译产生警告了,虽然它的运行结果是正确的。我们之前说过,一个优秀的程序员会将任何一个警告都看作是错误,因为它的结果将是不确定的。这样写是由问题的,洽好 g++ 编译器支持这样写,我们不能写出依赖于某种编译器特性的代码。所以这时便会用到初始化列表了,在 C++ 中提供了初始化列表对成员变量进行初始化,它的语法规则如下

初始化列表的使用(十五)_C++_03

        那么我们在程序中加上构造函数并用初始化列表进行初始化,如下

Test() : ci(5)
{    
}

        我们再次编译,如下

初始化列表的使用(十五)_C++_04

        我们看到编译器没有报任何警告,这便证明了在 C++ 中提供了初始化列表对成员变量进行初始化。那么我们在这块有几个注意事项:a> 成员的初始化顺序与成员的声明顺序相同;b> 成员的初始化顺序与初始化列表中的位置无关;c> 初始化列表先于构造函数的函数体执行。为例更加形象的说明,我们再次以代码为例进行说明

#include <stdio.h>

class Value
{
private:
    int mi;
public:
    Value(int i)
    {
        printf("i = %d\n", i);
        
        mi = i;
    }
    int getMI()
    {
        return mi;
    }
};

class Test
{
private:
    Value m2;
    Value m3;
    Value m1;
public:
    Test() : m1(1), m2(2), m3(3)
    {
        printf("Test::Test()\n");
    }
};

int main()
{
    Test t;
    
    return 0;
}

        我们在 Test 类中定义了 3 个 Value 对象,然后在构造函数中使用了初始化列表对他们进行初始化。在构造函数中我们加入了一条打印语句,按照我们之前讲的,应该在初始化完了之后再打印那条语句。我们编译看看结果

初始化列表的使用(十五)_列表_05

        我们看到打印的是如我们分析的那样,但是前面初始化的顺序好像不太一样,我们是按照 m1、m2、m3 这样的顺序进行初始化的。想想我们之前说的:初始化的顺序和它声明的顺序相同,和它的位置并无关。所以看看我们声明顺序就知道打印的是正确的了。

        类中的 const 成员会被分配空间的,它的本质是只读变量并且只能在初始化列表中指定初始值。编译器无法直接得到 const 成员的初始值,因此无法进入符号表成为真正意义上的常量。我们以代码为例进行说明

#include <stdio.h>

class Test
{
private:
    const int ci;
public:
    Test() : ci(10)
    {
        printf("Test::Test()\n");
    }
    
    int getCI()
    {
        return ci;
    }
    
    int setCI(int v)
    {
        int* p = const_cast<int*>(&ci);
        
        *p = v;
    }
};

int main()
{
    Test t;
    
    printf("t.ci = %d\n", t.getCI());
    
    t.setCI(100);
    
    printf("t.ci = %d\n", t.getCI());
    
    return 0;
}

        我们看看编译结果是否改变了 ci 的值

初始化列表的使用(十五)_初始化_06

        我们看到已经成功的通过指针 + const_cast去掉了它的 const 属性,也就是说,它只是一个具有只读属性的变量。我们再次强调下,初始化与赋值不同。初始化是对正在创建的对象进行初值设置,而赋值则是对已经存在的对象进行值设置。通过对初始化列表的学习,总结如下:1、类中可以使用初始化列表对成员进行初始化;2、初始化列表先于构造函数体执行;3、类中可以定义 const 成员变量,const 成员变量必须在初始化列表中指定初值,const 成员变量为只读变量


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083