C++11入门

2023-05-16

文章目录

  • 1. C++11简介
  • 2. 列表初始化
    • 2.1 initializer_list
    • 2.2 小结
  • 3. 声明
    • 3.1 auto
    • 3.2 decltype
    • 3.3 nullptr
  • 4. 范围for
    • 4.1 使用
    • 4.2 使用条件
  • 5. STL新容器
    • 5.1 array
    • 5.2 forward_list
    • 5.3 unordered_map和unordered_set
    • 5.4 字符串转换

1. C++11简介

链接:C++11官方文档

C++11是C++的一次重大更新,它新增了许多现代高级语言特性:自动类型推导、智能指针、lambda表达式…

本文只对C++11的部分基本特性作介绍,以上特性均在后续博客中。

C++标准委员会是一个妥妥的“鸽王”,在2003年,C++11的预命名是C++07,然而在2006年委员会觉得肯定完成不了,于是将命名改为C++0x,表示在2010年之前发布。然而2010年重蹈覆辙,直到2011年才“顺利”发布C++11标准。

C++11(C++ 2.0)新特性包括语言和标准库两个层面。C++11标准库的头文件可以不带.h,例如:<cstdio>,同时也兼容C语言风格。

2. 列表初始化

C++98标准:允许使用{}对数组或结构体中成员进行初始化:

struct Person
{
	int age;
	string name;
};
main()
{
	// 对数组
	int arr1[] = { 1,2,3 };
	int arr2[3] = { 0 };
	// 对结构体成员
	Person p = { 18, "小明" };
	return 0;
}

C++11标准:除了自定义类型之外,内置类型也可以用列表初始化,也可以不加上赋值符号:

int a1 = { 1 };
int a2{ 2 };

int array1[]{1, 2, 3 };
int array2[3]{0};

也可以用在new表达式中,但{}和new搭配使用就不能再加赋值符号了,因为前面已经有一个了:

int* p1 = new int[3]{ 0 };
int* p2 = new int[3]{ 1,2,3 };

实际上列表初始化就是调用对应类型的构造函数。

2.1 initializer_list

官方文档:initializer_list

image-20230103190930166

initializer_list是一个初始化容器,它存储的数据会被用于初始化其他容器。这是C++11实现列表初始化的前提,这也是C++98不支持列表初始化的原因。

例如,使用初始化列表实例化一个数组,然后查看它的类型:

int main()
{
    auto a = {1, 2, 3};

    cout << typeid(a).name() << endl;
    return 0;
}

输出:

class std::initializer_list<int>

从它的接口数量和功能来看,它就是一个临时容器。它不是专门用于存储数据的,它的名字已经说明了它的作用。

所有类型都支持它构造对象。本来是构造一个匿名对象,然后再调用构造函数,但是编译器优化后直接调用构造函数,然后隐式类型转换。

例如在C++11版本的vector容器构造函数中就增加了初始化列表为参数:

image-20230103191910651

那么对于vector、map这样的多参数容器,就可以使用{}进行列表初始化构造对象。当用列表对容器进行初始化时,这个列表({}中的内容)被编译器推导成initializer_list类型,然后调用这个新增的构造函数对该容器进行初始化。这个新增的构造函数要做的就是遍历initializer_list中的元素,然后将这些元素依次插入到要初始化的容器中即完成初始化容器。

那么,针对我们自定义类型,要实现列表初始化的前提就是重载参数为initializer_list的构造函数。

2.2 小结

C++11推出的列表初始化虽然可以适用于所有类型,但是依然建议普通对象按照原有方式初始化,另有需要的容器可以使用更方便的列表初始化。例如list容器本push比较麻烦,就可以使用列表初始化。

3. 声明

C++11推出了多种简化声明的方式。

3.1 auto

auto原本是C++98的一个关键字,它原本的功能是声明变量为自动变量。「自动变量」拥有「自动的生命周期」,然而就算不使用auto声明变量,变量依旧拥有自动的生命周期:

int a = 0;          // 拥有自动生命周期
auto int b = 1;     // 拥有自动生命周期
static int c = 2;   // 延长生命周期

auto声明变量并没有改变变量的生命周期,因为生命周期取决于变量何时创建和销毁,而这已经被默认确定了。它是变量的默认属性。因此C++11赋予auto以新的意义:自动类型推断。

要知道,在定义一个变量或实例化一个对象时,都要明确指明变量或对象的类型,假如这个变量或对象的类型比较长,或者忘记对象的名字且仍然记得这个对象的实例化方式,就可以使用auto关键字以自动推断变量或对象的类型。

int main()
{
    int b = 1;
    auto pb = &b;

    cout << typeid(b).name() << endl;
    cout << typeid(pb).name() << endl;

    return 0;
}

输出:

int
int *

而且auto的使用还能避免出现类型不合适的情况:

int main()
{
	short a = 32767;
	short b = 32767;

	auto c = a + b;

	cout << typeid(c).name() << endl;
	return 0;
}

输出:

int

如果在定义变量时忽略了它的范围,那么很有可能出现范围小的变量装不下的情况。

typeid能得到变量的类型,那么它能不能定义变量呢?

答案是否定的,如果想得到变量类型的同时,定义相同类型的变量,可以使用decltype关键字。

3.2 decltype

  • 关键字decltype可以将变量的类型声明为表达式指定的类型。
int main()
{
	int a = 0;
	decltype(a) b = 1;

	cout << typeid(b).name() << endl;
	return 0;
}

输出:

int

decltype还能推断表达式的类型:

template<class T1, class T2>
void func(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
}
int main()
{
	int x = 1;
	double y = 0.1;

	decltype(x * y) ret;
	decltype(&x) px;
	cout << typeid(ret).name() << endl;
	cout << typeid(px).name() << endl;

	func(1, 0);
	func(1, 0.2);

	return 0;
}

输出:

double
int *
int
double

推演函数返回值的类型:

int* func(int x)
{
	return (int*)x;
}
int main()
{
	cout << typeid(func).name() << endl;
	cout << typeid(func(1)).name() << endl;
	return 0;
}

输出:

int * __cdecl(int)
int *

注意:

  1. __cdecl(int)表明仅为函数名,不包含括号和参数;
  2. 如果包含参数列表和括号,推导的是函数的返回值类型,且并不会调用函数。

还可以指定函数的返回类型(本部分需要结合后面的内容):

template<class T1, class T2>
auto Add(T1 t1, T2 t2)->decltype(t1 + t2)
{
	decltype(t1 + t2) sum;
	sum = t1 + t2;
	cout << typeid(sum).name() << endl;
	return sum;
}
int main()
{
	cout << Add(1, 1) << endl;
	cout << Add(1, 1.1) << endl;
	return 0;
}

输出:

int
2
double
2.1

其中,auto Add(T1 t1, T2 t2)->decltype(t1 + t2)表示auto返回值的类型是decltype(t1 + t2)。

3.3 nullptr

在C语言中,NULL是一个空指针:

#define NULL    ((void *)0)

它的类型是void*

然而在C++中NULL被定义为0

/* Define NULL pointer value */
#ifndef NULL
    #ifdef __cplusplus
        #define NULL    0
    #else  /* __cplusplus */
        #define NULL    ((void *)0)
    #endif  /* __cplusplus */
#endif  /* NULL */

原因是C++不支持void*隐式转换成其他指针类型,因而void*类型在C++中就起不到空指针的作用:

int main()
{
	void* x = (void*)0;

	char* a = x;		// 欲将 void*->char*
	
	return 0;
}

C++中会出现这样的错误:
image-20230104190748189

将源文件重命名为.c文件,则编译通过。

C++引进nullptr关键字的原因是:在极端情况下,函数重载的调用会因为NULL的字面值0而发生匹配错误:

void func(char* c)
{
	cout << "func(char* c)" << endl;
}
void func(int c)
{
	cout << "func(int c)" << endl;
}
int main()
{
	func(NULL);
	return 0;
}

输出:

func(int c)

显然,调用func函数的参数NULL已经表明了我们想调用的是参数为char*的函数,然而事实并非如此。原因是源文件在编译链接的时候会进行宏替换,NULL会被替换为它的字面值0,在编译器眼里,这个调用就是这样的:

func(0);

调用void func(int c)也就不难理解了。

C++为了解决这样的匹配问题,将nullptr作为空指针关键字。

注意,这是NULLnullptr本质上的区别:

  • NULL是一个宏,它的值是0;
  • nullptr是C++中的关键字。

4. 范围for

4.1 使用

范围for的使用能让我们更方便地遍历元素。

for循环后的括号由冒号分为两部分:

  1. 用于接收每个元素的变量
  2. 迭代的范围
int main()
{
	int arr[5] = { 1, 2, 3, 4, 5 };

	for (int e : arr)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

for (int e : arr)表示遍历整个arr数组,并用一个int类型的变量接收每个元素。

范围for常与auto关键字结合使用:

for (auto e : arr)

如果遇到比较大的变量,可以使用引用接收以减少临时拷贝的开销:

for (auto& e : arr)

4.2 使用条件

  1. 范围确定:对于数组,它的长度需要明确;对于类,需要提供begin和end迭代器。因为范围for本质上就是使用迭代器遍历元素。
  2. 迭代的对象要支持++和==操作:原因同上,因为范围for本质就是使用迭代器,所以要遍历元素,就要支持++,要停下来,就要支持==

5. STL新容器

C++11中新增了四个容器:array、forward_list、unordered_map和unordered_set。

5.1 array

本质是一个静态数组,和原生的静态数组 区别不同的是它对越界访问检查十分严格,因为底层上array是用断言检查的,而原生数组并没有对每个空间检查,而是设定哨兵位。

array容器的operator[]接口如果发现有越界情况发生,会调用at接口抛出异常。

而且array容器的对象是创建在栈上的,因此array容器不适合定义太大的数组。而且人们已经习惯使用原来的数组,而且如果要保证安全性,vector足矣,因此它并没有被广泛使用。

5.2 forward_list

本质是一个单链表,没有被广泛使用。、

原因是:

  1. 它只支持头插,因此尾插尾删时需要先找尾,时间复杂度为O(N);
  2. 它只支持在指定元素后面插入,接口名为insert_after,如果要在元素前面插入元素,还要遍历整个链表,时间复杂度为O(N);
  3. 它只支持在指定元素后面删除,接口名为erase_after,原因同上,时间复杂度为O(N)。

5.3 unordered_map和unordered_set

它们底层使用和哈希表,因此查找效率非常高,可以认为是O(1),是查找效率最高的数据结构。

作者曾经的文档:

unordered_set和unordered_map的使用【STL】

用同一个哈希表实现unordered_map和unordered_set(C++实现)【STL】

5.4 字符串转换

C++11补充了string类型和内置类型相互装换的接口,最常用的有:to_string,其次是stoi、stol、stod等。

  • 内置类型转换为string

官方文档:std::to_string

  • string转换成内置类型

官方文档:Convert from strings

image-20230110234848231

C++11为每个容器都增加了一些新接口,例如:

  • 提供了一个以initializer_list作为参数的构造函数,用于支持列表初始化;
  • 提供了cbegin和cend方法,用于返回const迭代器;
  • 提供了emplace系列方法,并在容器原有插入方法的基础上重载了一个右值引用版本的插入函数,用于提高向容器中插入元素的效率。

关于右值引用等,是C++11最重要的特性,会在后续文档中着重介绍。

  • 内置类型转换为string

官方文档:std::to_string

  • string转换成内置类型

官方文档:Convert from strings
在这里插入图片描述

C++11为每个容器都增加了一些新接口,例如:

  • 提供了一个以initializer_list作为参数的构造函数,用于支持列表初始化;
  • 提供了cbegin和cend方法,用于返回const迭代器;
  • 提供了emplace系列方法,并在容器原有插入方法的基础上重载了一个右值引用版本的插入函数,用于提高向容器中插入元素的效率。

关于右值引用等,是C++11最重要的特性,会在后续文档中着重介绍。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11入门 的相关文章

随机推荐

  • C++静态成员静态函数,语法+示例,超详细!!

    类的静态成员定义 静态成员是指用static定义的变量 就是成员变量前面加了个 static 静态变量分为全局变量和局部变量两种 静态全局变量 静态全局变量声明在程序所有函数之外 xff0c 其作用域与全局变量一样 xff0c 所有函数体都
  • 数据结构。顺序栈的一些操作(两种定义、进栈、出栈、获取栈顶、共享栈)

    栈的两种定义 define MAXSIZE 50 静态 define ERROR 0 typedef struct int data MAXSIZE int top SqStack void init Stack SqStack amp s
  • 【无标题】数据结构。链栈的一些操作(定义、进栈、出栈)

    include lt iotream gt include lt stdio h gt include lt stdlib h gt using namespace std 构造节点 typedef struct StackNode int
  • 数据结构。栈+队列判断字符序列是否属于回文数字或回文字符,非常详细

    精髓 xff0c 请仔细体会 bool test SqQueue amp Q Stack amp s char c while c 61 getchar 61 39 64 39 因为栈是先进后出 push Stack s c 队列是先进先出
  • 动画函数添加回调函数

    回调函数原理 xff1a 函数可以作为一个参数 将这个函数作为参数传到另一个函数里面 xff0c 当那个函数执行完之后 xff0c 再去执行传进去的这个函数 xff0c 这个过程就叫做回调 回调函数的位置 xff1a 写到定时器结束的位置
  • 数组向后移动M位(C语言)

    include lt stdio h gt int main int N M int a 100 scanf 34 d d 34 amp N amp M for int i 61 0 i lt N i 43 43 scanf 34 d 34
  • 数据结构——顺序表

    一 定义 顺序表是一种线性的存储结构 xff0c 采用一段连续的地址存储单元依次存放数据元素 xff0c 一般采用数组存储 顺序表一般可分为 xff1a 1 静态顺序表 xff1a 使用定长数组存储元素 2 动态顺序表 xff1a 使用动态
  • WSL2安装

    目录 什么是WSL2 xff1f 安装WSL导入镜像设置Linux用户信息如何在资源管理器查看文件 xff1f 参考链接 笔者使用环境 Windows11 22H28GB RAM512GB ROM 什么是WSL2 xff1f WSL2是Wi
  • 计分板-2021安徽省机器大赛程序设计赛道

    题目描述 Alice 和 Bob 在玩游戏 xff0c 两个人分别有一个计分板 xff0c 记录各自的得分 得分 X 的 字典序严格小于得分 Y xff0c 那么就认为得分 X 高于得分 Y Bob 想要自己的分数高 于 Alice xff
  • 九、Debian 10 SSH

    要求 1 安装 SSH 工作端口监听在 19210 2 仅允许 InsideCli 客户端进行 ssh 访问 其余所有主机的请求都应该拒绝 3 在 cskadmin 用户环境 InsideCli 下可以使用密钥免密码登录 并且拥有超级 管理
  • Oracle函数中常用的日期函数实用案例

    获取系统当前时间 select sysdate from dual select current date from dual select localtimestamp from dual 获取两天以后的时间 select sysdate
  • 十六、Debian 10 WEB服务(lighttpd)

    题目要求 1 安装 lighttpd 使用其他 web 平台 以下功能均不得分 2 启用 fastcgi php 模块 3 index php 网页内容显示当前服务器的日期和时间 刷新页面时间自动更 新 解题步骤 1 了解 lighttpd
  • 数组及字符处理(C语言复习)

    1 编写程序 从键盘上输入10个整数 xff0c 求其中最大值和最小值及其序号 例 xff1a 输入 xff1a 88 95 10 3 6 81 12 77 166 35 输出 xff1a 最大值 xff1a 166 xff0c 序号 xf
  • 如何用python获取单个文件 或 文件夹中所有文件的行数

    目录 一 获取单个文件的行数二 获取文件夹中所有文件的行数三 关于os walk 函数 一 获取单个文件的行数 本例展示获取单个txt文件中的行数 xff1a span class token comment 统计单个文件的行数 span
  • 保姆级教程,阿里云快速搭建个人网站

    首先想要搭建一个网站需要一个域名和服务器 xff0c 我们先去阿里云搜索这两个东西 xff0c 然后分别去购买一下 服务器这里有轻量级应用服务器和云服务器ECS都可以选择 我选择的是ECS xff0c 然后我们去购买 xff0c 产品区域选
  • C语言-实现栈的基本操作(顺序栈)

    下面用两种方式来构建顺序栈 xff0c 分别是将top定义为指针类型和将top定义成指针下标两种形式 xff0c 实现栈的基本操作 目录 方法一 xff1a 1 1结构定义 1 2 完整代码 1 3测试用代码 xff08 用来逐步测试以上栈
  • 电脑无法打开相机照片怎么解决?

    相机拍照后的照片 xff0c 大部分人把照片保存在电脑上 xff0c 这样就可以把相机的内存卡腾空出来进行新的一轮拍摄 最近有新朋友询问如果电脑上的照片打不开怎么办 xff1f 首先我们要了解什么情况下电脑的照片会打不开 xff0c 原因可
  • Ubuntu22.04网络连接不上的问题

    平台 xff1a virtualbox Ubuntu22 04 在VirtualBox虚拟机上Ubuntu莫名其妙的连不上网 xff0c 在网络搜寻并尝试各种解答后问题终于得以解决 网络连接启动未打开 xff1b 在设置里面应该将网络勾选
  • 如何在Linux中安装redis(图文教程,按照步骤可安装成功)

    目录 1 在Redis版本库 xff1a https download redis io releases 可根据自己的需求选择下载对应的版本 xff0c 然后直接下载 2 通过Xftp工具进行上传 xff0c 选择指定的应用拖到右侧对应的
  • C++11入门

    文章目录 1 C 43 43 11简介2 列表初始化2 1 initializer list2 2 小结 3 声明3 1 auto3 2 decltype3 3 nullptr 4 范围for4 1 使用4 2 使用条件 5 STL新容器5