c++11 新风格(转)

C++11(及现代C++风格) 过去的一年我在微软亚洲研究院做输入法,我们的产品叫“英库拼音输入法” (下载Beta版),如果你用过“英库词典” (现已更名为必应词典),应该知道“英库”这个名字(实际上我们的核心开发团队也有很大一部分来源于英库团队的老成员)。整个项目是微软亚洲研究院的自然 语言处理组、互联网搜索与挖掘组和我们创新工程中心,以及微软中国Office商务软件部(MODC)多组合作的结果。至于我们的输入法有哪些创新的 阅读详情
Stroustrup

C++11标准推出了很多有用的新特性,本文特别关注那些相比C++98更像是一门新语言的特性,理由是:

  • 这些特性改变了编写C++程序使用的代码风格和习语【译注 1】,通常也包括你设计C++函数库的方式。例如,你会看到更多参数和返回值类型为智能指针(smart pointer),同时也会看到函数通过值传递返回大型对象。你将会发现在大多数的代码示例中充斥着新特性的身影。例如,几乎每5行现代C++代码示例都会使用到auto。
  • C++11的其他特性也很棒。但是请先熟悉下面这些,正是由于这些特性的广泛使用使得C++11代码如同其他现代主流语言一样整洁、安全和高效,与此同时保持了C++传统的性能优势。

提示:

  • 与Strunk & White【译注 2】一样,本文只做概要总结而不是详尽基本原理和优缺点分析。详细分析请参见其他文章。
  • 本文会不断更新,主要变更及内容增加请参见文末变更历史。

译注:

1. Programming idiom:编程习语,在一种或多种编程语言中重复出现的表达形式,用来表示没有在编程语言中内置的简单的任务或者算法,也可以用来表示在编程语言中内置的不常用或者不典型的某个特性。编程习语也可以在更广泛的范围内使用,比如代指复杂的算法或者设计模式。

2. Strunk & White:代指William Strunk Jr.和E. B. White出版的”The elements of style”,中文版《风格的要素》一书,该书介绍了英语写作要遵循的基本风格。


auto

基于以下两个原因,尽可能使用auto:首先,使用auto会避免重复声明编译器已经知道的类型。

?
1
2
3
4
5
// C++98
map< int ,string>::iterator i = m.begin();
  
// C++11
auto i = begin(m);

其次,当使用未知类型或者类型名称不易理解时使用auto会更加便利,例如大多数的lambda函数【译注 3】——你甚至不能简单的拼写出类型的名字。

?
1
2
3
4
5
// C++98
binder2nd< greater< int > > x = bind2nd( greater< int >(), 42 );
  
// C++11
auto x = []( int  i) { return  i > 42; };

需要注意,使用auto并不改变代码的含义。代码仍然是静态类型【译注 4】,每个表达式的类型都是清晰和明确的;C++11只是不需要我们重复声明类型名。一些人刚开始可能会害怕在这里使用auto,因为感觉好像没有(重复)声明我们需要的类型就意味着可能会碰巧得到一个不同的类型。如果你想要明确地进行一次强制类型转换,没有问题,声明目标类型就好了。然而大多数情况下,只要使用auto就可以了;几乎不会出现错误地拿到一个不同类型的情况,即便出现错误,C++的强静态类型系统也会由编译器让你知道这个错误,因为你正试图访问一个变量没有的成员函数或是错误地调用了该函数。

译注:

3. lambda function (λ函数):编程语言支持λ函数/λ表达式可以使得代码更易于理解,同时也可以使得代码变得更简洁,关于λ函数的技术解释可以参考,维基百科labmda calculus,也可以从《从.NET中委托写法的演变谈开去(中):Lambda表达式及其优势》博客中得到直观的解释。

4. 动态类型语言(dynamic typing language)是指类型检查发生在运行期间(run-time)的语言。静态类型语言(static typing language)是类型检查发生在编译期间(compile-time)的语言。

智能指针:无须delete

请始终使用标准智能指针以及非占有原始指针(non-owning raw pointer)。绝不要使用占有原生指针(owning raw pointer)和delete操作,除非你是在实现自己的底层数据结构这种少见的情况下(即使在此时也需要在class范围内保持完好的封装)。如果只能够知道你是另一个对象唯一的所有者,请使用unique_ptr来表示唯一所有权。一个“new T”表达式会马上初始化另一个引用它的对象,通常是一个unique_ptr。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// C++11 Pimpl Idiom
class  widget {
widget();
~widget();
private :
class  impl;
unique_ptr<impl> pimpl;
};
  
// in .cpp file
class  impl {
:::
};
  
widget::widget()
: pimpl( new  impl() )
{
}
  
widget::~widget() = default ;

使用shared_ptr来表示共享所有权。推荐使用make_shared来有效地创建共享对象。

?
1
2
3
4
5
6
7
// C++98
widget* pw = new  widget();
:::
delete  pw;
  
// C++11
auto pw = make_shared<widget>();

使用weak_ptr来退出循环并且表示可选项(例如,实现一个对象缓存)

?
1
2
3
4
5
6
7
8
9
10
11
12
// C++11
class  gadget;
  
class  widget {
private :
shared_ptr<gadget> g; // if shared ownership
};
  
class  gadget {
private :
weak_ptr<widget> w;
};

如果你知道另一个对象存在时间会更长久并且希望跟踪它,使用一个非占有 (non-owning)原始指针。

?
1
2
3
4
5
6
7
// C++11
class  node {
vector< unique_ptr<node> > children;
node* parent;
public :
:::
};

nullptr

始终使用nullptr表示一个null指针值,绝不要使用数字0或者NULL宏,因为它们也可以代表一个整数或者指针从而产生歧义。

?
1
2
3
4
5
// C++98
int * p = 0;
  
// C++11
int * p = nullptr;

Range for

基于范围的循环使得按顺序访问其中的每个元素变得非常方便。

?
1
2
3
4
5
6
7
8
9
// C++98
for ( vector< double >::iterator i = v.begin(); i != v.end(); ++i ) {
total += *i;
}
  
// C++11
for ( auto d : v ) {
total += d;
}

非成员(nonmember) begin和end

始终使用非成员begin和end,因为它是可扩展的并且可以应用在所有的容器类型(container type),不仅仅是遵循了STL风格提供了.begin()和.end()成员函数的容器,甚至数组都可以使用。

如果你使用了一个非STL风格的collection类型,虽然提供了迭代但没有提供STL的.begin()和.end(),通常可以为这个类型编写自己的非成员begin和end来进行重载。这样你就可以使用STL容器的编程风格来遍历该类型。C++11标准提供了示例数组就是这样一个类型,标准同时为数组提供了begin和end。

?
1
2
3
4
5
6
7
8
9
10
vector< int > v;
int  a[100];
  
// C++98
sort( v.begin(), v.end() );
sort( &a[0], &a[0] + sizeof (a)/ sizeof (a[0]) );
  
// C++11
sort( begin(v), end(v) );
sort( begin(a), end(a) );

Lambda函数和算法

Lambda函数是决定乾坤的因素,它会使你编写的代码变得更优雅、更快速。Lambda使得STL算法的可用性提高了近100倍。新近开发的C++函数库都是基于lambda可以用的前提(例如,PPL)并且有些函数库甚至要求你编写lambda来调用函数库(例如,C++ AMP)

下面是一个快速示例:找到v里面大于x并且小于y的第一个元素。在C++11中,最简单和干净的代码就是调用一个标准函数。

?
1
2
3
4
5
6
7
8
// C++98: 直接编写一个循环 (使用std::find_if会非常困难)
vector< int >::iterator i = v.begin(); // 因为我们需要在后边用到i
for ( ; i != v.end(); ++i ) {
if ( *i > x && *i < y ) break ;
}
  
// C++11: use std::find_if
auto i = find_if( begin(v), end(v), [=]( int  i) { return  i > x && i < y; } );

想要使用C++编写一个循环或者类似的新特性?不用着急;只要编写一个模板函数(template function)(函数库算法),并且几乎可以将lambda当做语言特性来使用,与此同时会更加灵活,因为它不是固定的语言特性而是一个真正的函数库。

?
1
2
3
4
// C#
lock ( mut_x ) {
... use x ...
}
?
1
2
3
4
5
6
7
8
9
10
11
// 不使用lambda的C++11:已经非常简洁并且更灵活(例如,可以使用超时以及其他选项)
{
lock_guard<mutex> hold( mut_x );
... use x ...
}
  
// 使用了lambda的C++11可以带一个辅助算法:在C++中使用C#的文法
// 算法:template<typename T, typename F> void lock( T& t, F f ) { lock_guard<T> hold(t); f(); }
lock( mut_x, [&]{
... use x ...
});

去熟悉lambda吧。你会不断用到它,不仅仅在C++中——lambda已经广泛应用于很多主流的编程语言。一个开始的好去处请参考我在PDC2010的演讲《无处不在的lambda》

Move / &&

Move被认为是copy的最佳优化,尽管它也使得其他事情成为可能比如信息被转发。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
// C++98:避免copy的替代方法
vector< int >* make_big_vector(); // 选择1: 返回指针: 没有拷贝,但不要忘记delete
:::
vector< int >* result = make_big_vector();
void  make_big_vector( vector< int >& out ); // 选择2: 通过引用传递: 没有拷贝,但是调用者需要传入一个有名对象
:::
vector< int > result;
make_big_vector( result );
  
// C++11: move
vector< int > make_big_vector(); // 通常对于”被调用者(callee)分配的空间“也适用
:::
vector< int > result = make_big_vector();

Move语法改变了我们设计API的方式。我们可以更多地设计通过值传递。为你的类型启用move语法,使用时会比copy更有效。

更多变化

还有更多现代C++的特性。并且我计划在未来编写更多深入C++11新特性以及其他特性的短文,我们会知道更多并且喜欢上它。

但目前,这是必须知道的新特性。这些特性组成了现代C++风格的核心,使得C++代码看起来和执行时像他们设计的那样,你将会看到这些特性会出现在几乎每一段你看到或者编写的现代C++代码中。并且它们使得现代C++更加干净、安全且快速,使得C++在未来的若干年仍然是我们产业的依靠。


C++编程风格 1.指针的算术运算 对vector、array等内存连续的容器进行取值操作: 函数传入参数的不同类型 ----模板 内存连续 ----指针 阅读详情

相关推荐

C++11(及现代C++风格)和快速迭代式开发

过去的一年我在微软亚洲研究院做输入法,我们的产品叫“英库拼音输入法” (下载Beta版),如果你用过“英库词典”(现已更名为必应词典),应该知道“英库”这个名字(实际上我们的核心开发团队也有很大一部分来源于英库团队的老成员)。整个项目是微软亚洲研究院的自然语言处理组、互联网搜索与挖掘组和我们创新工程中心,以及微软中国Office商务软件部(MODC)多组合作的结果。至于我们的输入法有哪些创新的fe

刘未鹏|C++的罗浮宫 7万+

C++11 现代C++风格的新元素(转)

Stroustrup C++11标准推出了很多有用的新特性,本文特别关注那些相比C++98更像是一门新语言的特性,理由是: 这些特性改变了编写C++程序使用的代码风格和习语【译注 1】,通常也包括你设计C++函数库的方式。例如,你会看到更多参数和返回值类型为智能指针(smart pointer),同时也会看到函数通过值传递返回大型对象。你将会发现在大多数的代码示例中充斥着新特性的身...

aigao1992的博客 201

细谈 C++ 零参数初始化对象三种风格:()、{} 和不加括号

一、引言 想必只要接触了 C++ 一段时间的开发者,一定会对于这么三种 C++ 零参数初始化对象的风格有所迷惑: IntCell *m; // 第 1 种风格:后面加小括号 m = new IntCell(); // 第 2 种风格:后面加大括号 m = new IntCell{}; // 第 3 种风格:后面什么也不加 m = new IntCell; 我可以很明确的告诉你,在零参数初始...

(ÒωÓױ) 5308

c++11标准风格

【代码】c++11标准风格。

tankeven的博客 213

CSAPP performance lab

CSAPP 深入理解计算机系统的performance Lab

weixin_46091531的博客 1253

告别手动处理:用 Bruker NSMatlabUtilities 工具箱批量分析 AFM 力曲线数据的实战案例

本文介绍了如何使用 Bruker NSMatlabUtilities 工具箱实现 AFM 力曲线数据的批量分析,解决传统手动处理耗时且易出错的问题。通过实战案例展示工具箱的核心功能,包括数据读取、力曲线分析和可视化,帮助科研人员提升数据处理效率和准确性。

weixin_28362173的博客 214

C++中的函数——常见的样式

常见的函数样式有四种:1.无参无返2.有参无返3.无参有返4.有参有返示例运行结果。

shuguang25800的博客 222

Linux实战:rMATS可变剪切分析从零到一,避坑指南与结果深度解析

本文详细介绍了在Linux系统上从零开始部署rMATS进行可变剪切分析的完整流程,包括环境搭建、安装避坑指南、参数优化及结果解读。特别针对gfortran依赖、数学库配置等常见问题提供解决方案,并深入解析JC与JCEC结果的差异及可视化技巧,帮助生物信息学研究者高效完成可变剪切分析。

weixin_30394669的博客 410

uv-cpp:基于libuv实现的C++11风格网络库

libuv是一个跨平台的的基于事件驱动的异步io库。但是他提供的功能不仅仅是io,包括进程、线程、信号、定时器、进程间通信等。libuv主要设计应用于Nodejs,由于其卓越的性能,越来越多的知名项目也使用了这一库。libuv是一个C语言库,意味着回调函数是C语言的回调,所以直接使用libuv网络消息回调,相关对象必须是全局的或者static对象,作为一个C++开发者,这是令人难以容忍的。于是本文将要介绍的uv-cpp应运而生。uv-cpp是基于libuv实现的C++11风格网络库。

草上爬的博客 3796

【没有网络,安装python包】如何从 PyPI 下载并安装 Plotly 的离线包

在某些情况下,您可能需要在没有互联网连接的环境中安装 Python 包,例如在飞机上、在某些受限制的网络环境中或在需要离线安装的场景下。以下是如何在 PyPI(Python Package Index)下载 Plotly 包并进行离线安装的详细步骤。

赖德发的博客 1104

【论文阅读】AU检测|《Deep Adaptive Attention for Joint Facial Action Unit Detection and Face Alignment》

摘要 本文旨在从大量没有人工标注的视频中学习用于面部动作单元检测的区分表示。受面部动作是面部肌肉运动这一事实的启发,论文将这些运动描述为不同帧中两幅面部图像之间的转换,并将其用作学习表示的自我监控信号。但是,在不受控制的情况下,这种转换是由面部动作和头部运动引起的。为了消除头部运动的影响,我们提出了一个双圈自动编码器(TCAE),它可以解开面部动作相关运动和头部运动相关运动。具体而言,TCAE被训练成分别将源面部的面部动作和头部姿态改变为目标面部的面部动作和头部姿态。我们的实验验证了TCAE解耦运动的能力。

Celine的博客 4554

cpp-channel:为C++11带来Go风格的并发编程

cpp-channel:为C++11带来Go风格的并发编程 项目介绍 cpp-channel 是一个为 C++11 提供类似 Go 语言风格的并发编程模型的库。它通过 cpp::channel 类型的通道,使得在多个线程之间进行通信变得简单而高效。这个库的设计理念来源于 Go 语言的并发模型,它通过通道(channel)的概念来简化线程间的数据传递,避免了传统多线程编程中的复杂性和不确定性。 项目...

gitblog_00489的博客 986

芒果YOLOv12改进108:基于QARep提出多种<独家原创结构>:ReNLANQARep, QARep, CSCQARep, C3QARep, C2fQARep等,即插即用提升小目标检测

芒果YOLOv12改进108:基于QARep提出多种<独家原创结构>:ReNLANQARep, QARep, CSCQARep, C3QARep, C2fQARep等,即插即用提升小目标检测

包括YOLOv5、YOLOv7、YOLOv8等模型改进 755

uv-cpp: 基于C++11的高性能量子网络库

`uv-cpp` 是一个简洁且高性能的网络库,它基于 `libuv` 实现,并采用了现代 C++ (C++11) 的特性进行封装。`libuv` 本身就是一个跨平台的事件驱动异步 I/O 库,不仅限于处理输入/输出操作,还包括进程管理、线程控制、信号响应及定时器等功能。 ### 主要特点: - **C++11 风格**:采用 C++11 功能性和绑定式的回调机制代替传统的 C 样式函数指针。 ...

gitblog_00189的博客 669

【XCharts插件】3-1、制作图表及代码生成图表(v1.0)

XCharts插件是一款基于UGUI的功能强大、易用、参数可配置的数据可视化图表插件。【Unity3D插件】XCharts插件完全教程《全网最全》-专栏文章目录OK,这是v1.0版本的XCharts插件的创建图表,代码生成图表的教程。可能有人要问了,既然都更新到v3.0了,还写啥v1.0教程呢。因为有些项目就是用v1.0去制作图表了,也没有太多的精力去更新插件,所以就一直用的v1.0,而且v1.0已经能实现大部分的图标样式了,所以,博主还是单独写了一篇文章来介绍v1.0的功能。

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 1293

北斗导航 | ILS(仪表着陆系统)、MLS(微波着陆系统)和GBAS(地基增强系统)的性能对比

最 早 的 GBAS 系 统 是 **局 域 增 强 系 统(LAAS)**,它是 1990 年由美国 Honeywell 公司 和 NASA 共 同 完 成 的 差 分 GPS(DGPS)演 变 而 来 的。1998 年,LAAS 被 ICAO 采 纳为 GBAS。2009 年 9 月 3 日 Honeywell 公 司 的GBAS 系统通过了 **CAT-I 精密进近着陆系统**的设计认证 ,而 **CAT-II 和 CAT-III 系统的研发和验证工作也正在有序的进行中**

尘世冰封的专栏 3987

现代C++风格-C++11

Original:Elements of Modern C++ Style “C++11 feels like a new language.” – Bjarne Stroustrup The C++11 standard offers many useful new features. This page focuses specifically and only on

fengxinze的专栏 1400

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

探索高效网络编程:uv-cpp —— libuv 的 C++11 实现

在软件开发的世界里,高效的网络库是构建高性能应用程序的关键组件之一。uv-cpp 是一款基于 libuv 的 C++11 网络库,以其简洁的接口、出色的性能和稳定性,在众多开源项目中脱颖而出。这个项目不仅提供了基础的 TCP 和 UDP 支持,还包含了 DNS 查询、HTTP 协议处理、定时器以及异步操作等功能,是构建网络应用的理想选择。 ## 项目介绍 uv-cpp 是一个专为 C++ 开发...

gitblog_00314的博客 1137

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【C++】C++11新内容

委托构造函数是一个特性,而不是特定的一个构造函数。它的功能是通过在初始化列表将构造过程委托给其他构造函数来实现的构造函数,主要用于避免代码重复,提高代码复用性和可维护性。特殊的地方在于,在构造函数中调用了另一个构造函数。class A{private:public:A(int a): A(a,0,0){} // 委托构造函数// 错误示范A(int a, int b): A(a), _b(b), _c(0){} // 同时混用了 成员初始化 和 委托构造函数。

weixin_45795947的博客 836

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C++ 风格指南

C++ 风格指南

zhaominyong的专栏 1044

UTF8-CPP跨版本兼容性指南:从C++98到C++20的完整支持

UTF8-CPP是一个轻量级的C++库,专注于以可移植的方式提供UTF-8编码和解码功能,支持从C++98到C++20的全版本C++标准。无论是处理多语言文本还是确保跨平台字符编码一致性,这个库都能为开发者提供可靠的解决方案。 ## 为什么选择UTF8-CPP? 在C++开发中,字符编码处理常常是跨平台项目的痛点。UTF8-CPP通过模块化设计,为不同C++标准提供针对性实现,确保代码在各种编

gitblog_01046的博客 932

C++11:读取properties文件的第三方库cpp_properties

在Java开发环境中JDK内置了java.util.Properties类用于读取.properties文件,在Java应用开发时广泛用于读取参数配置文件。 最近在C++环境下做一个项目设计,也希望能通过读取.properties文件来获取参数配置文件.在github上找到了这个C++11实现的读取.properties文件的项目github.com/glywk/cpp_properties 。完全支持Java properteis语法。 cpp_properties使用起来很简单,全部源码都是用C++11

10km的专栏 2034

pdf2zh 快速上手:2 条命令翻译 PDF 论文,排版公式全保留

读外文论文,译文一翻公式就散架,手工重排又没那个劲头?把这个丢给 pdf2zh。敲两条命令,拿回一份完全保留原版式的中文 PDF,外加一份中英对照。学生读文献、研究者追论文,直接用。 ## 两条命令跑通 先说清楚:Python 得是 3.11 或 3.12,装依赖时 onnxruntime、gradio 这些会自动带入,不用手装。 ```bash pip install pdf2zh pdf

gitblog_00373的博客 747

C++11新玩法

1 右值引用: int a[10]; for(auto& e: a) 默认成员函数的控制 1 语法上: 如果用户没有显示定义构造,拷贝构造等默成员函数,编译器会自动生成一个默认的成员函数。 实际上: 并不是,有的编译器会,但编译器不会。 eg VS2013会看情况而定,如果真的需要再生成,不是真的需要就不生成。 2 拿构造函数来说: 1 组合 : 如果A类含有B类对象,那么会...

快乐星球 1364

c++11 特性 for新用法

~~~~我的生活,我的点点滴滴!!

破茧 1114

【C++】C++11下线程库

本文就C++11线程库做详细的解读,C++11线程库本质就是对不同平台线程库的封装!给线程传可调用对象可以是函数指针、函数对象、lambda。多个线程共享同一份共享资源就会涉及线程安全的问题,因此使用mutex锁,还有可以不加锁的原子操作,并且可能因为一个线程竞争锁的能力太强会导致一直在加锁解锁,因此又有了condition_variable.

fight_p的博客 1598
上一篇: Eclipse FAQ/C++11 Features
下一篇: SVN中tag branch trunk用法详解(zhuan)
statdm
博客等级 码龄16年 195粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值