• 【C++】C++11——C++11介绍、初始化列表、声明、auto、decltype、nullptr、范围for循环


    C++11

    1.C++11介绍

    在这里插入图片描述

      C++11是C++编程语言的一个版本,于2011年发布。C++11引入了很多新特性,比如:类型推导(auto关键字)、Lambda表达式、线程库、列表初始化、智能指针、右值引用、包装器等等。 C++11带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正。

      这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多。总的来说,C++11使得C++更加现代化、易用和强大。

    C++11

                 

    2.初始化列表

    2.1{}初始化

      在C++98中,标准确实允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。这种初始化方式被称为“聚合初始化”(Aggregate Initialization)。

      对于数组,可以使用花括号{}将一组值列表初始化为数组的元素。

      对于结构体,如果结构体中的成员都是公有(public)的,并且没有定义构造函数,那么可以使用花括号{}将一组值列表初始化为结构体的成员变量。 例如:

    struct Point
    {
    	int _x;
    	int _y;
    };
    
    int main()
    {
    	int array1[] = { 1, 2, 3, 4, 5 };
    	int array2[5] = { 0 };
    	Point p = { 1, 2 };
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

      

      C++11扩大了初始化列表的使用范围,使其可以用于所有的内置类型和用户自定义的类型。在C++11之前,初始化列表只能用于POD(Plain Old Data)结构体和数组。

      使用初始化列表时,可以选择添加等号(=)或不添加。如果添加等号,则称为“值初始化”,如果不添加等号,则称为“直接初始化”。 这两种方式在C++11中都是有效的。

      值初始化将使用默认值或零值来初始化对象,而直接初始化将使用指定的值来初始化对象。例如:

    struct Point
    {
    	int _x;
    	int _y;
    };
    
    int main()
    {
    	int x1 = 1;
    	int x2{ 2 };
    	int array1[]{ 1, 2, 3, 4, 5 };
    	int array2[5]{ 0 };
    	Point p{ 1, 2 };
    	
    	// C++11中列表初始化也可以适用于n ew表达式中
    	int* pa = new int[4]{ 0 };
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

      

      创建对象时也可以使用列表初始化方式调用构造函数初始化

    class Date
    {
    public:
    	Date(int year, int month, int day)
    		:_year(year)
    		,_month(month)
    		,_day(day)
    	{
    		cout << "Date(int year, int month, int day)" << endl;
    	}
    	
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	Date d1(2022, 1, 1); // old style
    	
    	// C++11支持的列表初始化,这里会调用构造函数初始化
    	Date d2{ 2022, 1, 2 };
    	Date d3 = { 2022, 1, 3 };
    	return 0;
    }
    
    • 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

                

    2.2 std::initializer_list

    std::initializer_list介绍

      std::initializer_list是C++11标准库中的一种类型,用于表示某种特定类型的值的数组。它是一种模板类型,定义std::initializer_list对象时,必须说明列表中所含元素的类型。

      std::initializer_list对象可以用于初始化其他对象,或者作为函数的参数进行传递。 和std::vector一样,std::initializer_list也是一种模板类型,但是它和std::vector有一些重要的区别。首先,std::initializer_list对象中的元素永远是常量值,无法改变它们的值。其次,std::initializer_list对象本身并不拥有它所包含的元素,它只是一个指向元素数组的指针,这意味着它不会进行内存分配或释放。

      在使用std::initializer_list时,可以使用花括号{}来创建一个初始化列表,例如:

    std::initializer_list<int> ilist = {1, 2, 3, 4, 5};
    
    • 1

      在这个例子中,我们创建了一个包含5个整数的std::initializer_list对象。

      

      另外,可以使用begin()和end()成员函数来获取指向初始化列表首元素和尾元素的指针, 例如:

    auto begin = ilist.begin();  
    auto end = ilist.end();
    
    • 1
    • 2

      这些指针可以用于遍历初始化列表中的元素。需要注意的是,由于std::initializer_list对象中的元素是常量值,因此不能使用指针修改它们的值。

      总的来说,std::initializer_list是一种方便的类型,可以用于初始化其他对象或作为函数参数进行传递。但是需要注意的是,它并不拥有它所包含的元素,而且元素值是常量,无法修改。

                

    3.声明

    3.1auto

      在C++11中,auto关键字被引入,用于自动类型推导。它允许编译器根据初始化表达式的类型来自动推断变量的类型,而不需要显式地指定变量的类型。

      使用auto关键字可以使代码更加简洁和易读,特别是在处理复杂的类型或模板元编程时。它还可以减少由于手动指定错误类型而引起的错误。

      下面是一些使用auto关键字的示例:

    #include   
    #include   
      
    int main() 
    {  
        // 自动推导整型变量的类型  
        auto i = 10;  
        std::cout << "i: " << i << std::endl;  
      
        // 自动推导浮点型变量的类型  
        auto f = 3.14;  
        std::cout << "f: " << f << std::endl;  
      
        // 自动推导复杂类型的变量  
        std::vector<int> vec = {1, 2, 3, 4, 5};  
        for (auto element : vec) {  
            std::cout << "element: " << element << std::endl;  
        }  
      
        return 0;  
    }
    
    //i: 10  
    //f: 3.14  
    //element: 1  
    //element: 2  
    //element: 3  
    //element: 4  
    //element: 5
    
    • 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

      
      在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。

    int main()
    {
    	int i = 10;
    	auto p = &i;
    	auto pf = strcpy;
    	cout << typeid(p).name() << endl;
    	cout << typeid(pf).name() << endl;
    	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
    	
    	//map::iterator it = dict.begin();
    	auto it = dict.begin();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

                

    3.2decltype

      在C++11中,decltype关键字被引入,用于在编译时推断表达式的类型。它允许编译器根据表达式的类型来推断变量的类型,而不需要显式地指定变量的类型。

      decltype的使用方式如下:

    decltype(expression) variable_name;
    
    • 1

      其中,expression是一个表达式,可以是变量、函数调用、算术运算等等。decltype会根据expression的类型来推断variable_name的类型。
      

      下面是一些使用decltype的示例:

    #include   
    #include   
      
    int main() {  
        int i = 10;  
      
        // decltype推导整型变量的类型  
        decltype(i) j = 20;  
        std::cout << "j: " << j << std::endl;  
      
        // decltype推导复杂类型的变量  
        std::vector<int> vec = {1, 2, 3, 4, 5};  
        for (decltype(vec)::size_type index = 0; index != vec.size(); ++index) 
        {  
            std::cout << "vec[" << index << "]: " << vec[index] << std::endl;  
        }  
      
        return 0;  
    }
    
    //j: 20  
    //vec[0]: 1  
    //vec[1]: 2  
    //vec[2]: 3  
    //vec[3]: 4  
    //vec[4]: 5
    
    • 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

      在上面的示例中,decltype关键字被用于推断变量j和index的类型。编译器根据表达式的类型自动确定变量的类型,从而使代码更加简洁和易读。 需要注意的是,在推导复杂类型时,可以使用decltype和作用域解析运算符::来指定类型的嵌套部分。

    // decltype的一些使用使用场景
    template<class T1, class T2>
    void F(T1 t1, T2 t2)
    {
    	decltype(t1 * t2) ret;
    	cout << typeid(ret).name() << endl;
    }
    
    int main()
    {
    	const int x = 1;
    	double y = 2.2;
    	decltype(x * y) ret; // ret的类型是double
    	decltype(&x) p;      // p的类型是int*
    	
    	cout << typeid(ret).name() << endl;
    	cout << typeid(p).name() << endl;
    	F(1, 'a');
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

                

    3.3nullptr

      在C++11之前,C++使用NULL宏来表示指针的“空”值。然而,NULL宏在某些情况下可能会导致歧义,因为它在不同的上下文中可能有不同的含义。为了解决这个问题,C++11引入了nullptr关键字,用于表示指针的“空”值。

    #ifndef NULL
    #ifdef __cplusplus
    #define NULL   0
    #else
    #define NULL   ((void *)0)
    #endif
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      nullptr是一种特殊类型的字面量,它可以被转换为任何其他指针类型,也可以被转换为bool类型(在这种情况下,它的值为false)。与NULL不同的是,nullptr是一种类型安全的指针空值,它不会引发整数溢出或其他与整数相关的问题。

      下面是使用nullptr的示例:

      ptr1被初始化为nullptr,而ptr2被初始化为0。然后,通过检查指针是否为nullptr来判断指针是否为空。使用nullptr可以使代码更加清晰和易于理解,因为它明确表示指针的值为“空”。

    #include   
      
    int main() {  
        int* ptr1 = nullptr; // 使用nullptr初始化指针  
        int* ptr2 = 0; // 使用0初始化指针(不推荐)  
      
        if (ptr1) {  
            std::cout << "ptr1 is not null" << std::endl;  
        } else {  
            std::cout << "ptr1 is null" << std::endl;  
        }  
      
        if (ptr2) {  
            std::cout << "ptr2 is not null" << std::endl;  
        } else {  
            std::cout << "ptr2 is null" << std::endl;  
        }  
      
        return 0;  
    }
    
    //ptr1 is null  
    //ptr2 is null
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

                

    4.范围for循环

      在C++11中,可以使用范围for循环来遍历容器中的元素。 范围for循环的语法如下:

    for (decltype(container)::value_type element : container) 
    {  
        // 循环体  
    }
    
    • 1
    • 2
    • 3
    • 4

      其中,container是一个容器对象,如std::vector、std::list等。decltype(container)::value_type表示容器中元素的类型。

      范围for循环会依次遍历容器中的每个元素,并将当前元素的值赋给循环变量element。在循环体内,可以使用element来访问当前元素的值。

      

      下面是一个使用范围for循环遍历std::vector并返回每个元素值的示例:

      getVector()函数返回一个std::vector对象。在main()函数中,使用范围for循环遍历该对象,并在循环体内返回当前元素的值。

    #include   
    #include   
      
    std::vector<int> getVector() {  
        std::vector<int> vec = {1, 2, 3, 4, 5};  
        return vec;  
    }  
      
    int main() {  
        std::vector<int> vec = getVector();  
        for (int element : vec) {  
            std::cout << "element: " << element << std::endl;   
        }  
        return 0;  
    }
    
    //element: 1  
    //element: 2  
    //element: 3  
    //element: 4  
    //element: 5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

                

  • 相关阅读:
    开创性的区块链操作系统项目——去中心化的战舰游戏
    【uni-app从入门到实战】组件和样式学习
    Jenkins CI/CD 持续集成专题二 Jenkins 相关问题汇总
    Web酒店管理系统(附源码及资源)
    无涯教程-Android - List View函数
    使用Promise.race()实现控制并发
    Docker Volume: 实现容器间数据共享与持久化的利器
    react和vue3使用hook对比
    如何选择合适的香港物理服务器?
    【SQL刷题】Day3----SQL必会的常用函数专项练习
  • 原文地址:https://blog.csdn.net/Crocodile1006/article/details/133515387