C++11的历史和统一的初始化列表

发布于:2025-08-12 ⋅ 阅读:(14) ⋅ 点赞:(0)

c++11的历史背景

c++历史

C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。

1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

c++标准发展

1979年,贝尔实验室的本贾尼等人试图分析unix内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为
C with classes。语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。

阶段 内容
C with classes 类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等
C++1.0 添加虚函数概念,函数和运算符重载,引用、常量等
C++2.0 更加完善支持面向对象,新增保护成员、多重继承、对象的初始化、抽象类、静态成员以及const成员函数
C++3.0 进一步完善,引入模板,解决多重继承产生的二义性问题和相应构造和析构的处理
C++98 C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美国标准化协会认可,以模板方式重写C++标准库,引入了STL(标准模板库)
C++03 C++标准第二个版本,语言特性无大改变,主要:修订错误、减少多样性
C++05 C++标准委员会发布了一份计数报告(Technical Report, TR1),正式更名C++0x,即:计划在本世纪第一个10年的某个时间发布
C++11 增加了许多特性,使得C++更像一种新语言,比如:正则表达式、基于范围for循环、auto关键字、新容器、列表初始化、标准线程库等
C++14 对C++11的扩展,主要是修复C++11中漏洞以及改进,比如:泛型的lambda表达式,autot的返回值类型推导,二进制字面常量等
C++17 在C++11上做了一些小幅改进,增加了19个新特性,比如:static_assert()的文本信息可选,Fold表达式用于可变的模板,if和switch语句中的初始化器等
C++20 自C++11以来最大的发行版,引入了许多新的特性,比如:模块(Modules)、协程(Coroutines)、范围(Ranges)、概念(Constraints)等重大特性,还有对已有特性的更新;比如Lambda支持模板、范围for支持初始化等
C++23 允许在编译时检测当前是否在常量求值上下文中执行,operator[] 支持多参数,简化条件编译的语法(#elifdef 和 #elifndef),允许在 constexpr 函数中使用 union 和 try-catch(但 catch 必须在编译时无实际副作用),增强标准库例如支持print(没错,就是Python的print)、支持网络库等
在C++23出来之前还有这样一篇博客讨论C++2x的最新特性:

(1 封私信) C++23的目标 - 知乎

即使是这样,现在很多公司主流使用还是C++98和C++11(标准更新需要编译器支持,但很多公司一旦支持新标准可能导致旧代码出现问题,重构代码需要经济、时间),所以不用刻意追求最新,重点将C++98和C++11掌握好,随着对C++理解不断加深,有时间可以去琢磨下更新的特性。

即使没有官方库,也有很多民间自研的第3方库,但官方做的东西口碑至少还有,所以质量能有保证。

c++11的历史

在2003年c++标准委员会曾经提交了一份技术勘误表(简称TC1),使得c++03这个名字已经取代了c++98称为c++11之前的最新c++标准名称。

不过由于c++03(TC1)主要是对c++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为c++98/03标准。

1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,c++国际标准委员会在研究c++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初这个标准叫c++ 07。

但是到06年的时候,官方觉得2007年肯定完不成c++ 07,而且官方觉得2008年可能也完不成。最后干脆叫c++ 0x。x的意思是不知道到底能在07还是08还是09年完成。

结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为c++11。

从c++0x到c++11,c++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。相比于c++98/03,c++11则带来了数量可观的变化,其中包含了约140个新特性,以及对c++03标准中约600个缺陷的修正,这使得c++11更像是从c++98/03中孕育出的一种新语言。

相比较而言,c++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以c++11要作为一个重点去学习。

c++11的英文文档,点击即可学习,后续内容大多参考这个链接。

c++11相对于c++98,多的东西:

  1. 新容器。即forward_listarrayunordered_mapunordered_set

    array虽然是静态数组,但很多功能vector都有,而且vector的功能还比array多,forward_list作为单链表在很多场景也可以用list代替,也就unordered_mapunordered_set好用,能在部分情况代替mapset

  2. 新增返回带const的迭代器cbegincend。但是实际意义不大,属于锦上添花的操作。因为beginend也可以返回const迭代器。

  3. 新的构造函数(initializer_list),使容器和内置类型数组一样可通过{}初始化成为可能。

  4. 新增右值引用和基于右值引用的移动构造、移动赋值和右值引用版本插入。例如右值引用版本插入比如vector::emplace_backvector::push_backmap::insertmap::emplace都是新引入的插入接口函数的右值版本。

  5. 改进可变参数。

  6. 改进和新增部分关键字。例如autodecltypedefaultdeletefinaloverride

  7. 新增委托构造函数。

  8. 新增lambda函数和包装器。

当然不止这些,还有智能指针和线程库等。

这里就简单介绍c++11新增的initializer_list类。

统一的初始化列表

{}初始化

在c语言,标准允许使用花括号{}对内置类型数组或者结构体元素进行统一的列表初始值设定。

#include<stdio.h>

struct Point {
    int _x;
    int _y;
};
int main() {
    int array1[] = { 1, 2, 3, 4, 5 };
    int array2[5] = { 0 };
    struct Point p = { 1, 2 };
    return 0;
}

在c++中,struct延续了这种设定,将这种通过{}进行初始化的行为叫列表初始化class只要成员的访问权限是public也可以这样使用。

但c++的classstruct都被改造成了类,若类内部的成员访问是私有,或类中有别的构造函数,则不能通过{}进行初始化。

#include<stdio.h>

struct Point {
    int _x;
    int _y;
};

struct Point2{
    int _x;
    int _y;
};

struct Point3{
	Point3(int x,int y)
		:_x(x),_y(y){ }
    int _x;
    int _y;
};

class Point4{
public:
    int _x;
    int _y;
};


struct Point5{
private:
    int _x;
    int _y;
};

class Point6{
    int _x;
    int _y;
};

int main() {
    struct Point p = { 1, 2 };//一般的结构体 
    Point2 p2 = { 3, 2 };//没有构造函数的类 
    //Point3 p3 = { 3, 2 };//有构造函数的类不能在c++98这么玩 
    Point4 p4 = { 3, 2 };//class定义的类只要权限许可就行 
    //Point5 p5 = { 3, 2 };//私有成员不可访问 
    //Point6 p6={1,2};//私有成员不可访问 
    return 0;
}

c++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型用户自定义的类型,使用初始化列表时,可添加等号=,也可不添加。但成员中有私有的就不能使用初始化列表。

int main() {
    int x1 = 1;
    int x2{ 2 };
    int array1[]{ 1, 2, 3, 4, 5 };
    int array2[5]{ 0 };
    struct Point p = { 1, 2 };//见上个代码
    Point2 p2 = { 3, 2 };
    Point3 p3 = { 3, 2 };//c++11允许 
    Point4 p4 = { 3, 2 };
    //Point5 p5 = { 3, 2 };//私有成员不可访问 
    //Point6 p6={1,2};//私有成员不可访问 
    // c++11中列表初始化也可以适用于new表达式中
    int* pa = new int[4]{ 0, 1 };
    return 0;
}

{}内的参数相对于类的成员数,可以少,但不能多。

#include<iostream>
using namespace std;

class Array{
public:
	int a;
	int b;
	int c;
};

int main() {
	//Array a={1,2,3,4};//参数多了不允许 
	Array a={1,2};
	cout<<a.a<<' '<<a.b<<' '<<a.c<<endl;
    return 0;
}

initializer_list

c++支持类的对象使用初始化列表,但和c语言的结构体一样,仅用于给每个成员赋初值。

#include<iostream>
using namespace std;

class Array{
public:
	int arr[10];
};

int main() {
	Array a={1,2,3};
	for(int i=0;i<10;i++)
		cout<<a.arr[i]<<' ';
    return 0;
}

c++11新增了initializer_list类模板。

initializer_list - C++ Reference

#include<iostream>
using std::cout;
using std::endl;

int main() {
    auto li = { 1,2,3 };
    cout << typeid(li).name() << endl;
    //输出class std::initializer_list<int>
    return 0;
}

它可以让容器和普通的内置类型数组一样支持初始化列表。和构造+拷贝构造的{}初始化不同,initializer_list不限制参数个数。

initializer_list是新增的类,它能将用户提供的{数据,数据,...}作为initializer_list的数据,一般是作为构造函数的参数,C++11对STL中的不少容器就增加了initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。

通过vector的构造函数来演示初始化列表的实质:

#include<iostream>
#include<initializer_list>
using std::cout;
using std::endl;
using std::initializer_list;//可以展开namespace std但没必要

namespace mystd
{
    template<class T>
    class vector {
    public:
        typedef T* iterator;
        vector(initializer_list<T> l) {
            _start = new T[l.size()];//申请属于vector的新空间
            _finish = _start + l.size();
            _endofstorage = _start + l.size();
            iterator vit = _start;
            typename initializer_list<T>::iterator
                lit = l.begin();//这里推荐用auto减少长度
            while (lit != l.end()) {
                *vit++ = *lit++;
            }
            //for (auto e : l)
            //   *vit++ = e;
        }
        vector<T>& operator=(initializer_list<T> l) {
            vector<T> tmp(l);
            std::swap(_start, tmp._start);
            std::swap(_finish, tmp._finish);
            std::swap(_endofstorage, tmp._endofstorage);
            return *this;
        }
        T& operator[](size_t pos) {
            return _start[pos];
        }
    private:
        iterator _start;
        iterator _finish;
        iterator _endofstorage;
    };
}

int main() {
    mystd::vector<int>arr = { 1,2,3,4,5 };
    for (int i = 0; i < 5; i++)
        cout << arr[i]<<' ';
    return 0;
}

可以这样理解:编译器识别出{}时会先找到一个常量存储区(即只读数据段 .rodata ,有的编译器是静态存储区),将{}内的数据存下来,然后将这些内容调用initializer_list的拷贝构造,隐式转换成initializer_list。在vector这个容器内部的形参为
initializer_list的构造函数,通过遍历这个initializer_list来初始化容器。

不只是vector,其他容器也都添加了initializer_list作为自身的构造函数。

例如map

#include<iostream>
#include<map>
using std::cout;
using std::endl;
using std::map;

int main() {
	//pair也支持用{x,y}初始化
	map<int, int>mp = { {1,2},{2,3},{3,5} };
	for (auto& x: mp)
		cout << x.first << ' ' << x.second << endl;
	return 0;
}

关于成员的初始化,实际就是给成员赋予缺省值,当程序调用构造函数时,初始化列表会通过缺省值来初始化对象。详细见类的默认成员函数——构造函数-CSDN博客


网站公告

今日签到

点亮在社区的每一天
去签到