C++新特性

1、C++11 中的新特性

C++11 引入了许多新特性,包括自动类型推导、lambda 表达式、右值引用等。下面介绍其中的一些重要特性。

1.1 自动类型推导(Type Inference)

C++11 中引入了 auto 关键字,它可以用于自动推导变量的类型。例如:

auto i = 10; // 推导为 int
auto name = "John"; // 推导为 const char*

自动类型推导使得代码更加简洁,并且可以避免显式指定类型的麻烦。

auto只能推导类型,推导出来的类型不能用来定义对象,decltype解决了这点,推导类型后可以用来定义对象。

#include<cstring>
int main()
{
int i = 10;
auto p = &i; decltype(p) pi;//int*
pi = &i;
cout << *pi << endl;//10
return 0;
}

1.2 Lambda 表达式

Lambda 表达式是一种用于定义匿名函数的语法。它可以在需要函数对象的地方使用,并且可以捕获上下文中的变量。例如:

auto sum = [](int a, int b) {
return a + b;
}; int result = sum(5, 3); // 调用 lambda 表达式

Lambda 表达式提供了一种简洁的方式来定义和使用函数对象,特别是在需要传递函数作为参数的情况下。

1.3 右值引用(Rvalue References)

C++11 引入了右值引用,它允许我们绑定到临时对象(右值),并且可以实现移动语义和完美转发。右值引用由双引号 && 表示。

什么是左值和右值:

  • 一般来说,位于= 前的表达式为左值;存储在内存中、有明确存储地址(可取地址)的数据;
  • 右值是指可以提供数据值的数据(不可取地址)。
int&& rvalue = 520; // 绑定到右值 520

右值引用的主要作用和意义如下:

1.3.1.移动语义:右值引用可以用于实现移动语义,即将一个对象的资源所有权从一个对象转移给另一个对象,而不需要进行深拷贝或浅拷贝。例如:

class MyString {
public:
// 移动构造函数
MyString(MyString&& other) noexcept
: data_(other.data_), size_(other.size_) {
other.data_ = nullptr;
other.size_ = 0;
} private:
char* data_;
size_t size_;
}; MyString str1("Hello"); // 创建一个 MyString 对象
MyString str2(std::move(str1)); // 将 str1 转移给 str2

在上述代码中,MyString 类定义了一个移动构造函数,用于将一个右值引用转移给一个新对象。在创建 str2 对象时,使用 std::move 函数将 str1 转移给 str2,从而避免了不必要的深拷贝和内存分配操作,提高了程序的性能和效率。

1.3.2完美转发:右值引用可以用于实现完美转发,即在函数调用时将参数按照原样转发给其他函数,从而避免了不必要的复制和拷贝操作。例如:

template<typename T>
void process(T&& arg) {
other_func(std::forward<T>(arg)); // 将 arg 按照原样转发给 other_func
}

在上述代码中,process 函数接受一个右值引用参数 arg,并使用 std::forward 函数将 arg 按照原样转发给 other_func 函数,从而避免了不必要的复制和拷贝操作,提高了程序的性能和效率。

1.4基于范围的for循环

在C++98/03中,不同的容器和数组遍历的方式不尽相同,写法不统一,也不够简洁,而C++11基于范围的for循环可以以简洁、统一的方式来遍历容器和数组,用起来也更方便了。

例如,传统for循环:

#include <iostream>
#include <vector>
using namespace std; int main() {
vector<int> t{ 1,2,3,4,5,6 };
for (auto it = t.begin(); it != t.end(); ++it) {
cout << *it << " ";
}
return 0;
}

C++11基于范围的for循环,语法格式:

for (decl : expr) {
// 循环体
}

decl表示遍历声明,在遍历过程中,当前被遍历到的元素会被存储到声明的变量中.

expr是要遍历的对象,它可以是表达式、容器、数组、初始化列表等。

使用基于范围的 for 循环遍历容器,示例代码如下:

#include <iostream>
#include <vector>
using namespace std; int main(void) {
vector<int> t{ 1,2,3,4,5,6 };
for (auto value : t) {
cout << value << " ";
}
return 0;
}

在上面的例子中,是将容器中遍历的当前元素拷贝到了声明的变量value中,因此无法对容器中的元素进行写操作,如果需要在遍历过程中修改元素的值,需要使用引用。

此外,对容器的遍历过程中,如果只是读数据,不允许修改元素的值,可以使用const定义保存元素数据的变量,在定义的时候建议使用const auto &,减少一次数据的拷贝,这样相对于auto效率要更高。

#include <iostream>
#include <vector>
using namespace std; int main(void) {
vector<int> t{ 1,2,3,4,5,6 };
for (const auto& value : t) {
cout << value << " ";
}
return 0;
}

1.5 智能指针

在C++中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针。C++11中基于RAII机制,设计提供了三种智能指针,使用这些智能指针时需要引用头文件<memory>

  • std::shared_ptr:共享的智能指针;
  • std::unique_ptr:独占的智能指针;
  • std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视 shared_ptr 的。

1.5.1 unique_ptr

std::unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr

// 通过构造函数初始化对象
{
unique_ptr<int> ptr1(new int(10));
// error
unique_ptr<int> ptr2 = ptr1;
}

但是可以通过函数返回给其他的 std::unique_ptr,还可以通过 std::move 来转译给其他的 std::unique_ptr,这样原始指针的所有权就被转移了,这个原始指针还是被独占的。

unique_ptr<int> func()
{
return unique_ptr<int>(new int(520));
} int main()
{
unique_ptr<int> ptr1(new int(10));
unique_ptr<int> ptr2 = move(ptr1);
unique_ptr<int> ptr3 = func(); // 这里的本质上是有RVO优化
return 0;
}

1.5.2 shared_ptr

shared_ptr的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。

shared_ptr进行初始化有三种方式:通过构造函数,std::make_shared函数以及reset方法。

shared_ptr<int> ptr0(new A);
shared_ptr<int> ptr1(new int(9));
shared_ptr<int> ptr2(ptr1); shared_ptr<int> ptr3 = make_shared<int>(520); shared_ptr<int> ptr4 = make_shared<int>(100);
shared_ptr<int> ptr5 = ptr4;
ptr5.reset(new int(200));

对于一个未初始化的共享智能指针,可以通过 reset 方法来初始化,当智能指针中有值的时候,调用 reset 会使引用计数减 1。

创建一个智能指针的时候,更推荐使用make_shared。内存分配的次数会比方法一少一次。

1.5.3 weak_ptr

std::weak_ptr 可以看做是 shared_ptr 的助手,它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符 * ->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视 shared_ptr 中管理的资源是否存在。

智能指针如果循环引用会导致内存泄露,比如下面的例子:

struct A {
public:
shared_ptr<B> bptr;
~A() {
cout << "class A is disstruct ..." << endl;
}
};
class B {
public:
shared_ptr<A> aptr;
~B() {
cout << "class B is disstruct ..." << endl;
}
}; {
shared_ptr<A> ap(new A);
shared_ptr<B> bp(new B);
std::cout << ap->use_count(); // 1
std::cout << bp->use_count(); // 1 ap->bptr = bp;
bp->aptr = ap;
std::cout << ap->use_count(); // 2
std::cout << bp->use_count(); // 2
} // 引用计数无法正常为0,资源无法释放,内存泄漏

weak_ptr 可以解决循环引用的问题:

class A {
public:
weak_ptr<B> bptr;
~A() {
cout << "class A is disstruct ..." << endl;
}
};
class B {
public:
shared_ptr<A> aptr;
~B() {
cout << "class B is disstruct ..." << endl;
}
}; {
shared_ptr<A> ap(new A);
shared_ptr<B> bp(new B);
std::cout << ap->use_count(); // 1
std::cout << bp->use_count(); // 1 ap->bptr = bp;
bp->aptr = ap;
std::cout << ap->use_count(); // 1
std::cout << bp->use_count(); // 1
}

1.5.4 性能与安全的权衡

使用智能指针虽然能够解决内存泄漏问题,但是也付出了一定的代价。以shared_ptr举例:

  • shared_ptr的大小是原始指针的两倍,因为它的内部有一个原始指针指向资源,同时有个指针指向引用计数。
  • 引用计数的内存必须动态分配。虽然一点可以使用make_shared()来避免,但也存在一些情况下不能够使用make_shared()
  • 增加和减小引用计数必须是原子操作,因为可能会有读写操作在不同的线程中同时发生。比如在一个线程里有一个指向一块资源的shared_ptr可能调用了析构(因此所指向的资源的引用计数减一),同时,在另一线程里,指向相同对象的一个shared_ptr可能执行了拷贝操作(因此,引用计数加一)。原子操作一般会比非原子操作慢。但是为了线程安全,又不得不这么做,这就给单线程使用环境带来了不必要的困扰。

https://en.cppreference.com/w/cpp/header/memory

2、C++14 中的新特性

C++14 对 C++11 进行了一些改进,并引入了一些新特性,例如变长模板参数、二进制字面量等。

2.1 变长模板参数(Variadic Templates)

C++14 允许定义可变数量的模板参数,这被称为变长模板参数。通过使用省略号 ...,可以在模板参数列表中指定任意数量的参数。例如:

template <typename... Args>
void print(Args... args) {
((std::cout << args << " "), ...);
} print(1, "hello", 3.14); // 输出: 1 hello 3.14

变长模板参数提供了更大的灵活性,可以处理不同数量和类型的参数。

2.2 二进制字面量(Binary Literals)

C++14 允许使用二进制字面量来表示二进制数值。使用前缀 0b0B,后跟一串二进制数字。例如:

int binary = 0b1010; // 二进制数值 10

二进制字面量提供了一种直观和简洁的方式来表示和使用二进制数值。

3、C++17 中的新特性

C++17 引入了一些有用的新特性,包括结构化绑定、折叠表达式、文件系统库等。

3.1 结构化绑定(Structured Bindings)

结构化绑定允许将元组或其他复杂类型的成员解包并绑定到独立的变量中。这样可以方便地访问和操作复杂类型的成员。例如:

std::pair<int, std::string> person{ 25, "John" };
auto [age, name] = person; std::cout << "Age: " << age << ", Name: " << name; // 输出: Age: 25, Name: John

结构化绑定简化了处理复杂类型的过程,使代码更加简洁易读。

3.2 折叠表达式(Fold Expressions)

折叠表达式是一种用于处理可变数量参数包的语法。它允许在模板展开过程中对参数包进行操作。例如:

template <typename... Args>
bool allTrue(Args... args) {
return (true && ... && args);
} bool result = allTrue(true, true, false); // 返回 false

折叠表达式提供了一种简洁的方式来处理参数包,可以在编译时对参数进行组合和计算。

3.3 文件系统库(Filesystem Library)

C++传统文件操作需要使用std::ifstreamstd::ofstream 类:分别表示输入文件流和输出文件流,可以用于读写文件内容。例如:

#include <fstream>
#include <iostream> int main() {
std::ifstream input_file("input.txt"); // 打开输入文件
if (!input_file.is_open()) {
std::cerr << "Failed to open input file.\n";
return 1;
} std::ofstream output_file("output.txt"); // 打开输出文件
if (!output_file.is_open()) {
std::cerr << "Failed to open output file.\n";
return 1;
} int num;
while (input_file >> num) { // 从输入文件中读取数字
output_file << num * 2 << '\n'; // 将每个数字乘以 2 并写入输出文件
} input_file.close(); // 关闭输入文件
output_file.close(); // 关闭输出文件 return 0;
}

C++17 引入了标准文件系统库,用于处理文件和目录的操作。该库提供了一组类和函数,用于创建、删除、移动、遍历文件和目录等操作。例如:

#include <filesystem>
namespace fs = std::filesystem; int main() {
fs::path dir_path = "dir"; // 定义目录路径
fs::create_directory(dir_path); // 创建目录 fs::path file_path = dir_path / "file.txt"; // 定义文件路径
fs::ofstream file(file_path); // 打开文件
if (!file.is_open()) {
std::cerr << "Failed to open file.\n";
return 1;
} file << "Hello, world!\n"; // 写入文件内容 file.close(); // 关闭文件 fs::remove(file_path); // 删除文件
fs::remove(dir_path); // 删除目录 return 0;
}

文件系统库简化了文件和目录操作的实现,使得操作更加方便和可移植。

4、C++20 中的新特性

C++20 引入了一系列新特性,包括概念、协程、三路比较运算符等。

4.1 概念(Concepts)

概念是 C++20 中的一项重要特性,用于对模板的类型参数进行约束。概念允许我们对类型进行条件检查,从而限制模板的实例化。例如:

template <typename T>
concept Arithmetic = std::is_arithmetic<T>::value; template <Arithmetic T>
T square(T value) {
return value * value;
} int result = square(5); // 正确,T 为算术类型 std::string str = "hello";
// 错误,T 不是算术类型
int result = square(str);

概念提供了一种声明式的方式来定义模板参数的约束条件,使代码更具表达力和安全性。

4.2 协程(Coroutines)

C++20 引入了协程支持,使得异步编程更加简洁和可读。协程允许函数在执行期间暂停和恢复,以便于异步任务的处理。例如:

#include <iostream>
#include <coroutine> struct Generator {
struct promise_type {
int current_value; auto get_return_object() {
return Generator{ std::coroutine_handle<promise_type>::from_promise(*this) };
} auto initial_suspend() {
return std::suspend_always{};
} auto final_suspend() noexcept {
return std::suspend_always{};
} void return_void() {} auto yield_value(int value) {
current_value = value;
return std::suspend_always{};
} void unhandled_exception() {
std::terminate();
}
}; std::coroutine_handle<promise_type> coroutine; bool move_next() {
coroutine.resume();
return !coroutine.done();
} int current_value() {
return coroutine.promise().current_value;
}
}; Generator generate() {
co_yield 1;
co_yield 2;
co_yield 3;
} int main() {
Generator generator = generate();
while (generator.move_next()) {
std::cout << generator.current_value() << " ";
}
// 输出: 1 2 3
return 0;
}

协程提供了一种简洁的方式来编写异步代码,提高了代码的可读性和可维护性。

4.3 三路比较运算符(Three-Way Comparison)

C++20 引入了三路比较运算符(<=>),用于比较对象的大小关系。它返回一个可比较的结果,可以是小于、等于或大于。例如:

struct Person {
std::string name;
int age; auto operator<=>(const Person& other) const = default; //auto表示函数的返回类型将由编译器自动推导.在这里,返回类型将会是一个 std::strong_ordering 类型,这是 C++20 引入的一种枚举类型,用于表示强制排序关系。 //= default:这个关键字表示使用默认实现,即使用编译器自动生成的代码来实现这个成员函数。在这里,编译器会自动生成一个使用 spaceship 运算符比较对象的代码。
}; Person john{"John", 25};
Person alice{"Alice", 30}; if (john < alice) {
std::cout << "John is younger than Alice.";
} else if (john > alice) {
std::cout << "John is older than Alice.";
} else {
std::cout << "John and Alice have the same age.";
}

三路比较运算符简化了比较操作的实现,提供了一种统一和直观的比较语法。

C++新版本特性的更多相关文章

  1. 用极简方式实现新浪新版本特性展示效果--view的图片轮播

    在发布版本的时候,大多数软件会在第一次使用新版本时候弹出视图用几张图片给用户做一个新版本特性介绍,最简单如下图新浪的版本特性介绍 由于图片是全屏展示且是左右滑动,大多数情况开发者会选择使用scroll ...

  2. delphi 各新版本特性收集

    delphi 各新版本特性收集 http://www.cnblogs.com/dreamszx/p/3602589.html

  3. Android新版本特性以及注意事项

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 了解新版本的特性还是很有必要的,为什么这么讲呢?因为可以从应用市场对发布应用的目标API版本(targetSdkVersion值)的 ...

  4. 小酌Jmeter4.0新版本特性

    1.  首先下载打开jmeter4.0,说一个能感受到的视觉变化,如图, 黑色界面,不少朋友认为做技术黑色的东西看起来高上大一点,虽然这个观念有点肤浅,但似乎也有点道理,毕竟还是有不少朋友热衷于lin ...

  5. scrapy新版本特性

    1:在spider中返回一个自定义的字典,老版本中需要先定义一个Item,填充后再返回一个对象 新版本中可以直接返回一个字典 2:Per-spider settings  为每个spider进行单独设 ...

  6. MySQL 8.x 新版本特性赶紧学!!Linux 服务器上安装 MySQL 8.x

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 引言 ...

  7. [iOS微博项目 - 1.7] - 版本新特性

    A.版本新特性 1.需求 第一次使用新版本的时候,不直接进入app,而是展示新特性界面 github: https://github.com/hellovoidworld/HVWWeibo       ...

  8. Java 8 正式发布,新特性全搜罗

    经过2年半的努力.屡次的延期和9个里程碑版本,甲骨文的Java开发团队终于发布了Java 8正式版本. Java 8版本最大的改进就是Lambda表达式,其目的是使Java更易于为多核处理器编写代码: ...

  9. (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

    本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...

  10. 业余草分享 Spring Boot 2.0 正式发布的新特性

    就在昨天Spring Boot2.0.0.RELEASE正式发布,今天早上在发布Spring Boot2.0的时候还出现一个小插曲,将Spring Boot2.0同步到Maven仓库的时候出现了错误, ...

随机推荐

  1. 从baselines库的common/vec_env/vec_normalize.py看reinforcement learning算法中的reward shape方法

    参考前文:https://www.cnblogs.com/devilmaycry812839668/p/15889282.html 2.  REINFORCE算法实际代码中为什么会对一个episode ...

  2. Hutool常用工具类

    1.背景 实际开发中经常用到很多的工具类,这里hutool提供了一系列的工具类,下面重点介绍常用的工具类. 2.使用步骤 官方文档:https://hutool.cn/docs/#/ 添加依赖 < ...

  3. 在 React 项目中 Editable Table 的实现

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:佳岚 可编辑表格在数栈产品中是一种比较常见的表单数据交互方 ...

  4. OpenTiny HUICharts开源发布,带你了解一个简单、易上手的图表组件库

    摘要:目前 OpenTiny HUICharts 已经成功落地在华为内部100多个产品中,持续提升了用户的可视化体验. 本文分享自华为云社区<OpenTiny HUICharts 正式开源发布, ...

  5. Layui Upload 多文件上传访问多次接口问题解决

    Layui 多文件上传访问多次接口 点我访问 LayUI框架官网 话不多数直接看源码 文件地址: layui/modules/upload.js // 通过each循环文件列表 layui.each( ...

  6. Kummer 定理

    \(n!\) 中含素数 \(p\) 的幂次为 \(\displaystyle\sum_{i=1}\lfloor\frac{n}{p^{i}}\rfloor\) Kummer 定理:\({n+m\cho ...

  7. 什么是淘宝API?

    ​ 淘宝API是淘宝开放平台提供给开发者的一系列应用程序编程接口,它们允许开发者访问和使用淘宝的数据和服务.通过这些API,开发者可以构建应用程序,实现商品信息检索.订单管理.用户行为分析.物流跟踪等 ...

  8. Vue Element-ui Table实现动态新增和删除

    达到效果:1.点击添加动态添加一行表格数据 2.点击移除删除一行 templete部分代码 <el-tab-pane label="股东情况"> <el-row& ...

  9. 推荐一款开源一站式SQL审核查询平台!功能强大、安全可靠!

    1.前言 在当今这个数据驱动的时代,数据库作为企业核心信息资产的载体,其重要性不言而喻.随着企业业务规模的不断扩大,数据库的数量和种类也日益增多,这对数据库的管理与运维工作提出了前所未有的挑战.在这样 ...

  10. Mac 删除 Steam 游戏图标

    Steam 将游戏安装在了 ~/Applications,打开访达,前往该目录,将相关游戏移到废纸篓.