C++11(一)(列表初始化,变量类型推导(auto,decltype),nullptr,范围for循环等)

news2024/9/24 17:18:22

目录

C++11简介

列表初始化

C++98中,{}的初始化问题

内置类型的列表初始化

自定义类型的列表初始化

变量类型推导

auto

decltype 

nullptr

范围for循环

final和override

默认成员函数的控制

显式缺省函数

删除默认函数


C++11简介

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不过由于TC1主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。从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有一些很重要的点需要我们掌握,其中包括智能指针,右值引用(移动构造,完美转发等),lambda表达式等,而这些我会在后面的章节单独讲解。

列表初始化

C++98中,{}的初始化问题

在C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定。比如:

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

对于一些自定义的类型,却无法使用这样的初始化。比如

vector<int> v{1,2,3,4,5};

这些STL库中的容器,vector,list,或者map啊等等,都不可以使用{}进行初始化,造成了非常大的麻烦,例如vector只能一个一个数据push_back(),非常的麻烦,所以C++11就支持了这种特性。

{}对于初始化数据的类型也分为两种:

内置类型的列表初始化

int main()
{
    // 内置类型变量
    int x1 = { 10 };
    int x2{ 10 };
    int x3 = 1 + 2;
    int x4 = { 1 + 2 };
    int x5{ 1 + 2 };
    // 数组
    int arr1[5]{ 1,2,3,4,5 };
    int arr2[]{ 1,2,3,4,5 };
    // 动态数组,在C++98中不支持
    int* arr3 = new int[5]{ 1,2,3,4,5 };
    // 标准容器
    vector<int> v{ 1,2,3,4,5 };
    map<int, int> m{ {1,1}, {2,2},{3,3},{4,4} };
    return 0;
}

以上都在C++11中被支持。

可以看到都已经被初始化。

 注意:列表初始化可以在{}之前使用等号,其效果与不使用=没有什么区别

自定义类型的列表初始化

看下面这一段代码:

class Point
{
public:
    Point(int x = 0, int y = 0) : _x(x), _y(y)
    {
        cout << "Point()" << endl;
    }
private:
    int _x;
    int _y;
};
int main()
{
    Point p1(1, 2);
    //c++11写法,最好不要这么写,但应该看懂,知道这么写什么意思。
    Point p2{ 1, 2 };
    Point p3 = { 1,2 };
    return 0;
}

p1,p2,p3都可以被成功初始化吗?

答案是可以的!而且都是调用构造函数进行初始化。我们可以运行一下:

 可以发现构造函数确实被调用了3次。

其实理论上说,p2和p3的{1,2}都应该先调用构造函数,然后调用拷贝构造给p2和p3,但编译器会直接优化成直接调用构造函数,相当于p1.

那说了半天,{}到底是个什么呢,也是个数据类型吗?

是的!它的名称是initializer_list,它也是一种数据类型。我们可以验证一下:

int main()
{
    auto x = { 1,2,3,4,5,6 };
    cout << typeid(x).name() << endl;
}

  而且库中对initializer_list也有说明:

   那它具体怎么用呢?库中提供了这些函数:

 begin(),end()这些,它可以像迭代器一样去使用。下面这个用例也说明了这个用法:

 for循环可以迭代器似的访问initializer_list里面的数据,

那么vector是如何支持它的呢?

 以及list,map等等,全部都支持了。

所有的容器都支持这样的一个构造函数,所以我们才可以使用列表初始化。

int main()
{
    //这里本质调用支持list(initializer_list<value_type> il类似这样的构造函数。
    vector<int> v{ 1,2,3,4,5 };
}

当然我们可以这样使用:

class Point
{
public:
    Point(int x = 0, int y = 0) : _x(x), _y(y)
    {
        cout << "Point()" << endl;
    }

    int _x;
    int _y;
};
int main()
{
    vector<Point> vp{ {1,2},{3,4} };
    for (auto e : vp)
    {
        cout << e._x << " " << e._y << endl;
    }
}

 可以看到已经成功初始化了。

或者这样:


    Point p1 = { 1,2 };
    Point p2 = { 3,4 };
    vector<Point> vp{ p1,p2};

两种方法都可以,map用来来会更加舒服一些。

总结:C++11以后一切对象都可以用列表初始化。但是建议普通对象用以前的方式初始化,容器如果有需求的话可以使用初始化列表。

变量类型推导

auto

在定义变量时,必须先给出变量的实际类型,编译器才允许定义,但有些情况下可能不知道需要实际类型怎么给,或者类型写起来特别复杂,比如:

    short a = 32670;
    short b = 32670;
    // c如果给成short,会造成数据丢失,如果能够让编译器根据a+b的结果推导c的实际类型,就不会存在问题. 
    short c = a + b;
    auto d = a + b;
    cout << c << endl;
    cout << d << endl;

 可以看到auto的结果是正确的。

还有一种情况是数据类型过于繁琐,很长.

    std::map<std::string, std::string> m{ {"apple", "苹果"}, {"banana","香蕉"} };
    // 使用迭代器遍历容器, 迭代器类型太繁琐( std::map<std::string, std::string>::iterator )
    std::map<std::string, std::string>::iterator it = m.begin();
    while (it != m.end())
    {
        cout << it->first << " " << it->second << endl;
        ++it;
    }

这个数据类型(std::map<std::string, std::string>::iterator)这么长,写起来未免让人有些难受。所以我们直接改成auto即可.

这样也看起来很简洁,唯一不足的就是可能可读性会稍差一些。

decltype 

decltype是什么?

decltype是根据表达式的实际类型推演出定义变量时所用的类型.

它和typeid().name()和auto有什么区别呢?

先说和typeid().name()的区别:

typeid().name()可以得出变量或表达式的数据类型,但是不能利用推出的这个数据类型定义别的变量。

而decltype不仅可以得出变量或表达式的数据类型,而且还可以利用推出的这个数据类型定义新的变量。

例如:

    int x = 1;
    int y = 1;
    cout << typeid(x + y).name() << endl;
    //错误,typeid推出的数据类型不可以定义新的变量
    typeid(x + y).name() z = x + y;
    //正确,decltype推出的数据类型可以定义新的变量
    decltype(x + y) z = x + y;
    cout << z << endl;

 和auto的区别

decltype是根据其他表达式或变量推出的数据类型,进而来定义变量。

而auto是根据自身的表达式而推出的数据类型。

这个说法可能不太准确,但是意思确实是这样的。可以看下面的例子理解:

    int x = 1;
    //decltype是根据x推出的类型为int,所以z1类型为int
    decltype(x) z1 = 20.23;
    //auto是根据自身20.23自动推出类型为double,所以z2类型为double
    auto z2 = 20.23;
    cout << z1 << endl;
    cout << z2 << endl;

看完这些,相信大家已经对decltype的概念理解了吧。

总的来说,decltype可以根据推导出的类型去定义新的变量。

nullptr

在之前,C++中的NULL被定义成了字面量0,这可能会带来一些问题,因为0既能表示指针常量,又能表示整型常量。

比如有有两个重载函数,一个函数参数类型是int,另一个是int*.

而你传入参数NULL,按我们理解来说,应该是调用参数为int*的这个函数,而编译器不会,它会嗲用参数为int的这个函数,这就会出现一些问题.

所以出于安全和清晰的考虑,C++11增加了新的关键字nullptr,用于表示空指针。

内部是((void*)0).

范围for循环

这个建议大家参考我之前写的C++入门基础下有详细的语法和用法,这里就不再过多赘述。

final和override

这个也是建议大家参考我写的C++多态中,有对这两个关键字的用法的详细说明

默认成员函数的控制

在C++中对于空类编译器会生成一些默认的成员函数,比如:构造函数、拷贝构造函数、运算符重载、析构函数和&和const&的重载、移动构造、移动拷贝构造等函数。如果在类中显式定义了,编译器将不会重新生成默认版本。有时候这样的规则可能被忘记,最常见的是声明了带参数的构造函数,必要时则需要定义不带参数的版本以实例化无参的对象。而且有时编译器会生成,有时又不生成,容易造成混乱,于是C++11让程序员可以控制是否需要编译器生成.

显式缺省函数

在C++11中,可以在默认函数定义或者声明时加上=default,从而显式的指示编译器生成该函数的默认版本,用=default修饰的函数称为显式缺省函数.

class A
{
public:
    A(int a) : _a(a)
    {}
    // 显式缺省构造函数,由编译器生成
    A() = default;
    // 在类中声明,在类外定义时让编译器生成默认赋值运算符重载
    A& operator=(const A& a);
private:
    int _a;
};
A& A::operator=(const A& a) = default;
int main()
{
    A a1(10);
    A a2;
    a2 = a1;
    return 0;
}

删除默认函数

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且不给定义,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

class A
{
public:
    A(int a) : _a(a)
    {}
    // 禁止编译器生成默认的拷贝构造函数以及赋值运算符重载
    A(const A&) = delete;
    A& operator(const A&) = delete;
private:
    int _a;
};
int main()
{
    A a1(10);
    // 编译失败,因为该类没有拷贝构造函数
    //A a2(a1);
    // 编译失败,因为该类没有赋值运算符重载
    A a3(20);
    a3 = a2;
    return 0;
}

本章先到此结束了,这只是C++的一小部分,而且不算的上很重要,后面我们讲右值引用,智能指针时会进行更加详细的讲解。这些也是重中之重。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/731997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32F767-0-HAL库主从定时器产生固定数量的PWM

STM32F767-0-主从定时器产生固定数量的PWM 前言一、配置STM32F767主从定时器的详细步骤1.1 选择主定时器和从定时器&#xff1a;1.2 配置主定时器&#xff08;主从模式&#xff09;&#xff1a;1.2.1 设置主定时器的时钟源&#xff1a;1.2.2 配置主定时器的分频系数&#xff1a…

不同路径(力扣)动态规划 JAVA

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; 示例 1&a…

JWT的简单说明与使用

简要 JWT是"JSON Web Token"的缩写&#xff0c;是一种用于在不同系统之间传输信息的开放标准。它通过将信息进行加密后生成一个安全的令牌&#xff0c;以便在网络请求中进行身份验证和授权。 具体来说&#xff0c;JWT可以用于以下几个方面&#xff1a; 身份验证&…

CMakeLists.txt 语法介绍

CMake编译原理 CMake是一种跨平台编译工具,主要编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件&#xff0c;最后用make命令编译源码生成可执行程序或共享库.因此CMake的编译基本就两个步骤&#xff1a;cmake && make cm…

MySQL基础练习

Ⅰ Ⅱ 3.1 3.2 3.3 3.4 -- 单表查询练习 /* 素材 CREATE TABLE emp ( empno int(4) NOT NULL, ename varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, job varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, …

细数那些Compose新手容易犯的错误

作者&#xff1a;晴天小庭 笔者作为一个日常Jetpack Compose开发者&#xff0c;对Compose的理解也在逐渐加深中&#xff0c;最近回顾当初学习和实践的过程中&#xff0c;犯了不少错误和踩了很多坑&#xff0c;本篇文章作为小总结分享给大家&#xff0c;同时文章会持续更新&…

Leetcode-每日一题【1669.合并两个链表】

题目 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。 请你将 list1 中下标从 a 到 b 的全部节点都删除&#xff0c;并将list2 接在被删除节点的位置。 下图中蓝色边和节点展示了操作后的结果&#xff1a; 请你返回结果链表的头指针。 示例 1…

Unity游戏C# dll注入

案例游戏下载 首先在网上下载个游戏案例&#xff0c;我就以Captain Molly游戏为例。 这个游戏玩家默认生命值有5点&#xff0c;咱们通过dll注入修改为10点。 dnSpy 我使用dnSpy来查看Unity游戏生成的dll代码&#xff0c;当然你们也可以使用其他工具。 Unity游戏脚本代码最终…

Codeforces-Round-883-Div-3

Codeforces Round 883 (Div. 3) 链接&#xff1a;https://codeforces.com/contest/1846 A. Rudolph and Cut the Rope There are n n n nails driven into the wall, the i i i-th nail is driven a i a_i ai​ meters above the ground, one end of the b i b_i bi​ m…

Pinia: vue的最新状态管理库

Pinia: vue的最新状态管理库&#xff0c;vuex状态管理工具的替代品。 pinia官方文档 注意defineStore()的返回值还是一个方法&#xff0c;所以useCounterStore是一个方法&#xff0c;执行该方法得到一个对象。 getters: 异步action: storeToRefs: 补充 vuex&#xff…

20中文字符识别(matlab程序)

1.简述 随着计算机科学的飞速发展&#xff0c;以图像为主的多媒体信息迅速成为重要的信息传递媒介&#xff0c;在图像中&#xff0c;文字信息(如新闻标题等字幕) 包含了丰富的高层语义信息&#xff0c;提取出这些文字&#xff0c;对于图像高层语义的理解、索引和检索非常有帮助…

Zero-Shot Node Classification

零样本节点分类(Zero-shot node classification) 谱图卷积 图卷积网络 GCN的分解

pandas中比较两个对象相等性 .eq()函数

在使用pandas做数据分析时&#xff0c;往往我们会有这样的数据需求&#xff1a;为某有某一属性的客户打标签。此刻&#xff0c;.eq()函数&#xff0c;就可以实现它自身的价值。 Lets go&#xff0c;一起去探索它的神秘力量吧&#xff01; 先讲讲它的用途&#xff1a;可以用于…

银河麒麟高级服务器系统部署-尚文网络xUP楠哥

进Q群11372462领取专属报名福利! # 什么是银河麒麟 银河麒麟高级服务器操作系统是针对企业级关键业务&#xff0c;适应虚拟化、云计算、大数据、工业互联网时代对主机系统可靠性、安全性、性能、扩展性和实时性等需求&#xff0c;依据CMMI5级标准研制的提供内生本质安全、云原…

Pandas理论与实战(一)

目录 一、Series对象 1.1 认识Series对象 1.2 Series对象的索引 1.3 获取Series的索引和值 二、DataFrame对象 2.1 认识DataFrame对象 2.2 DataFrame重要属性和函数 ​三、导入外部数据 3.1 导入.xls或.xlsx文件 3.2 导入csv文件 3.3 导入.txt文本文件 3.4 导入HTML网页…

Sentinel组件限流降级

官网: home | Sentinel 文档不是很全, 关于nacos的配置中心的使用完全没有 常见的限流算法 静态窗口限流: 即规定1秒内只能固定处理多少请求动态窗口限流: 同样是规定1秒内处理多少请求, 但是统计方式与第一个不同, 比如2.5秒则是统计1.5秒到现在的请求数漏桶限流: 进来可以…

【全栈第三课】通过ChatGPT快速入门NodeJS

前言 往期全栈课程&#xff1a; Vue从入门到精通 微信小程序从入门到精通 Node.js基础 简介 Node.js是什么&#xff1f; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O的模型&#xff0c;使其轻量又高效。Node.js …

迎接高考的倒计时网页(❤️好看好用❤️)HTML+CSS+JS

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

并发编程_jmm部分

1. JMM 理解 前提&#xff1a;并发编程有3大问题&#xff0c;可见性、有序性、原子性。 导致可见性的原因是缓存&#xff0c;有序性的原因是 编译器优化。解决方法就是直接禁用缓存和编译器优化&#xff0c;导致程序性能堪忧。 因此合理的方案就是按需禁用缓存和编译器优化。 …