zoukankan      html  css  js  c++  java
  • 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
    ", 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()
    ");
        }
    };
    
    
    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
    ", 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()
    ");
        }
        int getCI()
        {
            return ci;
        }
        int setCI(int v)
        {
            int* p = const_cast<int*>(&ci);
            
            *p = v;
        }
    };
    
    
    int main()
    {
        Test t;
        
        printf("t.ci = %d
    ", t.getCI());
        
        t.setCI(10);
        
        printf("t.ci = %d
    ", 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
    ", mi);
        }
        Test(const Test& obj)
        {
            mi = obj.mi;
            printf("Test(const Test& obj): %d
    ", 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
    ", mi);
        }
        Test(const Test& obj)
        {
            mi = obj.mi;
            printf("Test(const Test& obj): %d
    ", 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
    ", 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
    ", mi);
        }
        Test(const Test& obj)
        {
            mi = obj.mi;
            printf("Test(const Test& obj): %d
    ", 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的使用顺序
    • 全局对象的构造顺序是不确定的
    • 对象的析构顺序与构造顺序相反
  • 相关阅读:
    Js 循环 forEach, map, for, for...in, for...of
    es6 let const
    es6 Symbol类型
    es6 Reflect 与 Proxy
    es6 Map&Set
    es6箭头函数
    es6数组Arrary
    学写网站(一)前端配置之安装nvm、node、npm
    python获取当前执行代码所在文件的地址、主程序所在地址
    scrapy中的选择器下载中间件downloadmiddlewares
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10079718.html
Copyright © 2011-2022 走看看