当前位置: 首页 > article >正文

C++之C++新特性

目录

  • C++11
    • auto
    • for_each
    • 就地初始化
    • 非静态成员的sizeof
    • final
    • override
    • initialize_list
    • 提高类型安全
  • C++14
  • C++17
    • 基本语言特性
      • 结构化绑定
    • 新属性和属性特性
      • [[nodiscard]]
      • [[maybe_unused]]
      • [[fallthrough]]
    • 模板特性
    • 新的标准库组件
      • std::optional<>
      • std::variant<>
      • std::any #include<any>
      • std::byte #include <cstddef>
    • 文件系统库

C++11

连续两个右尖括号>中间空格隔开,C++98会优先解析为右移,C++11会优先解析为模板参数界定符

auto

  • auto声明变量的类型必须由编译器在编译时期推导而得;auto声明的变量必须被初始化;优势是可以匹配返回类型,因为有时候不确定返回类型,就会声明错误
  • 自动类型推导的一种新写法:auto func(T params) -> type;可以配合decltype关键字:template<typename U, typename V>auto add(U a, V b) -> decltype(a+b)

for_each

  • C++11中新增long long类型(其实在之前就支持了),标准要求long long类型可以在不同的平台上有不同的长度,但至少64位

就地初始化

只能使用"=“或”{}"进行初始化;作用先于列表初始化;如果既有列表初始化又有就地初始化,最终值取决于列表初始化

非静态成员的sizeof

在C++98中对非静态成员变量使用sizeof是不能通过编译的,需要对象的实例才能对其成员进行sizeof操作;C++11可以——有什么用?

struct People{ int hand; static People *all; }
People p;
cout << sizeof(p.hand) << endl;         // 都支持
cout << sizeof(People::all) << endl;    // 都支持
cout << sizeof(People::hand) << endl;   // C++11才支持

final

  • 禁止继承:将类标记为final,方法是直接在类名后使用关键字final,这样继承该类将导致编译出错
  • 禁止重写:在成员函数的参数列表后使用final关键字,使派生类不可覆盖它所修饰的虚函数(防止重载和重写)

override

虚函数描述符,如果派生类在虚函数声明时使用了override,该函数必须重写其基类中的同名函数,用在子类重写的函数后面!!声明和定义在一起的时候不能使用?

initialize_list

  • 标准库中容器对初始化列表的支持源自 #include<initializer_list> 这个头文件中initialize_list类模板的支持
  • 自定义类型使用列表初始化需要包含该头文件,并且声明一个initialize_list模板类为参数的构造函数
  • 函数的参数列表也可以使用初始化列表,即以initializer_list作为模板参数
  • 初始化列表可以用于函数返回的情况。返回一个初始化列表通常会导致构造一个临时变量,当然列表初始化构造成什么类型依据返回类
  • 使用初始化列表的一大优势防止类型收窄——类型收窄一般是一些使得数据变化或者精度丢失的隐式类型转换

提高类型安全

  • 强类型枚举,在enum后加上关键字class,具有以下优势:
    强作用域:强类型枚举成员的名称不会被输出到其父作用域空间
    转换限制:不可与整型隐式的相互转换
    可以指定顶层类型:具体方法是在枚举名称后面加上“:type”,如 enum class Type: char { … }
    匿名的enum class可能什么都做不了
  • nullptr: 是一个“指针空值类型”的常量,指针空值类型被命名为 nullptr_t,所以可以通过nullptr_t来声明一个指针空值类型的变量(使用nullptr_t的头文件 #include<cstddef>
    所有定义为nullptr_t类型的数据都是等价的,行为也是完全一致
    nullptr_t类型数据可以隐式转换为任意一个指针类型
    nullptr_t类型数据不能转换为非指针类型,即使是使用reinterpret_cast<nullptr_t>()的方式
    nullptr_t类型数据不适用于算术运算表达式

C++14

前缀0b允许定义二进制字面量
数字分隔符可以增强数字字面量的可读性
变量模板

C++17

基本语言特性

结构化绑定

概念:允许用一个对象的元素或成员同时实例化多个实体,形如:

void struct_bind() {
    // 绑定到map这个对象
    map<int, string> m;
    m.insert({1, "hello"});
    m.insert({2, "world"});
    m.insert({3, "good"});
    for(const auto& [key, val]: m) {
        std::cout << key << ": " << val << "\n";
    }
    // 绑定到struct对象
    struct MyStruct {
        int i = 0;
        std::string s;
    };
    MyStruct ms;
    auto [u, v] = ms;
}

为了理解结构化绑定,必须意识到这里面其实有一个隐藏的匿名对象,结构化绑定时新引入的局部变量名其实都指向这个匿名对象的成员

auto [u, v] = ms;
auto e = ms;
aliasname u = e.i;
aliasname v = e.s;

使用修饰符
结构化绑定适用的场景
a. 对于所有非静态成员都是public的结构体和类,可以把每个成员绑定到一个新的变量名上
b. 对于原生数组,可以把数组的每个元素绑定到新的变量名上
c. 对于任何类型,可以使用tuple-like API来绑定新的名称,std::tuple, std::pair, std::array

// 带初始化的if和switch语句
int b = 20;
if(int a = 10; b != a) {
    cout << a << endl;
    return;
}

if语句的条件表达式里定义的变量将在整个if语句中有效
可以在加锁的地方使用
内联变量
可以在头文件中以inline的方式定义全局变量
内联变量产生的动机:C++不允许在类里初始化非常量静态成员,可以在类定义的外部定义并初始化非常量静态成员,但如果被多个cpp文件同时包含的话又会引发新的错误,根据一次性定义原则,一个变量或实体的定义只能出现在一个编译单元内,除非该变量或实体被定义为inline
对于静态成员,在C++17中constexpr修饰符现在隐含着inline

static constexpr int n = 5;  // 等价于  
inline static constexpr int n = 5;
// 聚合体
struct Data {
    std::string name;
    double value;
};
// 聚合体初始化
Data x = {"test1", 6.778};
// 在C++11中起可以忽略等号
Data x {"test1", 6.778};
// 自C++17起聚合体可以拥有基类
struct MoreData : Data {
    bool done;
};
// 初始化时可以用如下两种方式:
MoreData y {{"test1", 6.778}, false };
MoreData y { "test1", 6.778, false };

C++17中聚合体的定义:
是一个数组,或者,
满足如下条件的类类型:

新属性和属性特性

[[nodiscard]]

鼓励编译器在某个函数的返回值未被使用时给出警告,但并不意味着编译器必须这么做

[[maybe_unused]]

可以避免编译器在某个变量未被使用时发出警告。新的属性可以应用于类的声明,使用typedef,using定义的类型,变量等场景

void foo(int val, [[maybe_unused]] std::string str) {
    ...
}

class MyClass {
   char c;
   int i;
   [[maybe_unused]] char xxx[32];
};

注意:不能在一条语句上应用[[maybe_unused]]

[[fallthrough]]

可以避免编译器在switch语句中某一个标签缺少break语句时发出警告

模板特性

constexpr if: 编译器if语句

新的标准库组件

std::optional<>

头文件:#include<optional>, 模拟了一个可以为空的任意类型的实例,可以被用作成员,参数,返回值等
同时定义了以下:std::nullopt , 从 std::exception 派生的 std::bad_optional_access 异常类,当无值的时候访问值将会出现异常。可选对象也使用了头文件中定义的 std::in_place 对象来初始化多个参数的可选对象
操作:
• 构造/析构/=
• make_optional<>():创建一个用参数初始化的可选对象

std::variant<>

头文件:#include <variant>
说明:提供了一个新的联合类型,最大的优势是提供了一种新的具有多态性的处理异质集合的方法,也就是说可以处理不同类型的数据,并且不需要公共基类和指针
如果第一个类型没有默认构造函数,那么调用variant的默认构造函数将会导致编译期错误,为支持第一个参数没有默认构造的情况,C++标准库提供了std::monostate,可以作为第一个选项来保证variant能默认构造
可以从variant派生

std::any #include

说明:是一种在保证类型安全的基础上还能改变自身类型的值类型。也就是说,它可以持有任意类型的值,并且它知道当前持有的值是什么类型
std::any对象同时包含了值和值的类型。
为了将当前值转换为真实的类型,必须要使用any_cast<>,如果转换失败,可能是因为对象为空或者与内部值的类型不匹配,会抛出一个std::bad_any_cast 异常。
拷贝std::any的开销一般都很大,推荐以引用传递对象,或者move值,std::any支持部分move语义

std::byte #include

这个类型代表内存的最小单位,std::byte本质上代表一个字节的值,但不能进行数字或字符的操作,也不对每一位进行解释
注意:std::byte实现和unsigned char类似,不能保证8位;底层实现的类型是unsigned char,所以大小总为1
列表初始化是唯一可以直接初始化std::byte对象的方法
没有定义输入和输出运算符,因此不得不把它转换为整数类型再进行IO

文件系统库

#include, 命名空间:std::filesystem,一个很常见的操作时定义:namespace fs = std::filesystem
异常:std::filesystem::filesystem_error


http://www.kler.cn/news/17712.html

相关文章:

  • 借助PLC-Recorder,西门子PLC S7-200SMART实现2ms周期采集的方法(带时间戳采集)
  • C plus plus ——【继承与派生】
  • TouchGFX开发(2)----触摸屏幕组件点亮LED
  • 【Android入门到项目实战-- 8.5】—— 使用HTTP协议访问网络的实践用法
  • 命令设计模式
  • python基本数据类型---数字字符串
  • 有关教育的几段话
  • 基于zookeeper实现分布式锁
  • 【无人车】无人驾驶地面车辆避障研究(Matlab代码实现)
  • 特征向量中心度(Eigenvector Centrality)
  • Kali 安装中文输入法(超详细)
  • 中级软件设计师备考---软件工程1
  • git上传大大大文件项目好折磨人
  • k8s之审计日志
  • 微机作业题
  • 字节跳动测试岗面试挂在2面,复盘后,我总结了失败原因,决定再战一次...
  • (别再手动点APP了)UiAutomator2自动化测试框架带你玩转APP操作
  • 模拟银行账户转账业务
  • 【软件测试】测试分类
  • 《花雕学AI》28:革命性的 ChatGPT for SEO——让您的排名飙升 50%!
  • 人脸识别--传统+深度方法
  • 【GAMES101】04 Viewing Transformation
  • LVS+Keepalived 高可用群集部署
  • 如何发起一次完整的HTTP的请求流程
  • NLP模型(四)——seq2seq模型与Attention机制实现
  • TryHackMe-AD证书模板
  • 测试2:基础
  • MYSQL数据库
  • 《程序员面试金典(第6版)》面试题 16.11. 跳水板
  • Android多模块开发