C++解析(12):初始化列表与对象构造顺序、析构顺序

0.目录

1.类成员的初始化

2.类中的const成员

3.对象的构造顺序

4.对象的析构顺序

5.小结

1.类成员的初始化

类中是否可以定义const成员?

下面的类定义是否合法?如果合法ci的值是什么存储在哪里?

(会报错)

C++中提供了初始化列表对成员变量进行初始化
语法规则:

注意事项:

  • 成员的初始化顺序与成员的声明顺序相同
  • 成员的初始化顺序与初始化列表中的位置无关
  • 初始化列表先于构造函数的函数体执行

证明成员的初始化顺序与成员的声明顺序相同,而与初始化列表中的位置无关:

#include <stdio.h>

class Value
{
private:
    int mi;
public:
    Value(int i)
    {
        printf("i = %d\n", i);
        mi = i;
    }
    int getI() { 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;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
i = 2
i = 3
i = 1
Test::Test()

2.类中的const成员

类中的const成员:

  • 类中的const成员会被分配空间
  • 类中的const成员的本质是只读变量
  • 类中的const成员只能在初始化列表中指定初始化

编译器无法直接得到const成员的初始值,因此无法进入符号表成为真正意义上的常量。

证明类中的const成员的本质是只读变量:

#include <stdio.h>

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

class Test
{
private:
    const int ci;
    Value m2;
    Value m3;
    Value m1;
public:
    Test() : m1(1), m2(2), m3(3), ci(100)
    {
        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(10);
    
    printf("t.ci = %d\n", t.getCI());
    
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
i = 2
i = 3
i = 1
Test::Test()
t.ci = 100
t.ci = 10

初始化与赋值不同:

  • 初始化:对正在创建的对象进行初值设置
  • 赋值:对已经存在的对象进行值设置

3.对象的构造顺序

3.1 局部对象的构造顺序

对于局部对象——当程序执行流到达对象的定义语句时进行构造

下面程序中的对象构造顺序是什么?

示例:

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
};

int main()
{
    int i = 0;
    Test a1 = i;
        
    while( i < 3 )
    {
        Test a2 = ++i;
    }
        
    if( i < 4 )
    {
        Test a = a1;
    }
    else
    {
        Test a(100);
    }

    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
Test(int i): 0
Test(int i): 1
Test(int i): 2
Test(int i): 3
Test(const Test& obj): 0

以下程序改变了程序的执行流,有严重的bug,但是不同的编译器有不同的处理结果,有些编译器不会报错,一定要注意这种问题!

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
    int getMi()
    {
        return mi;
    }
};

int main()
{
    int i = 0;
    Test a1 = i; // Test(int i): 0
        
    while( i < 3 )
    {
        Test a2 = ++i; // Test(int i): 1, 2, 3
    }
goto End;       
        Test a(100);
End:
    printf("a.mi = %d\n", a.getMi());
    return 0;
}

3.2 堆对象的构造顺序

对于堆对象

  • 当程序执行流到达new语句时创建对象
  • 使用new创建对象将自动触发构造函数的调用

下面程序中的对象构造顺序是什么?

示例:

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
    int getMi()
    {
        return mi;
    }
};

int main()
{
    int i = 0;
    Test* a1 = new Test(i); // Test(int i): 0
        
    while( ++i < 10 )
        if( i % 2 )
            new Test(i); // Test(int i): 1, 3, 5, 7, 9
        
    if( i < 4 )
        new Test(*a1);
    else
        new Test(100); // Test(int i): 100
        
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
Test(int i): 0
Test(int i): 1
Test(int i): 3
Test(int i): 5
Test(int i): 7
Test(int i): 9
Test(int i): 100

3.3 全局对象的构造顺序

对于全局对象

  • 对象的构造顺序是不确定的
  • 不同的编译器使用不同的规则确定构造顺序

(要避开全局对象之间的相互依赖!)

4.对象的析构顺序

单个对象创建时析构函数的调用顺序——析构函数与对应的构造函数调用顺序相反

  1. 调用父类的构造过程
  2. 调用成员变量的构造函数(调用顺序与声明顺序相同)
  3. 调用类自身的构造函数

多个对象析构时——析构顺序构造顺序相反:

  • 对于栈对象和全局对象,类似于入栈与出栈的顺序,最后构造的对象被最先析构!!
  • 堆对象的析构发生在使用delete的时候,与delete的使用顺序相关!!

5.小结

  • 类中可以使用初始化列表对成员进行初始化
  • 初始化列表先于构造函数体执行
  • 类中可以定义const成员变量
  • const成员变量必须在初始化列表中指定初值
  • const成员变量为只读变量
  • 局部对象的构造顺序依赖于程序的执行流
  • 堆对象的构造顺序依赖于new的使用顺序
  • 全局对象的构造顺序是不确定的
  • 对象的析构顺序与构造顺序相反
posted @ 2018-12-06 21:34  PyLearn  阅读(1437)  评论(0编辑  收藏  举报