此文是如下博文的翻译:

https://herbsutter.com/elements-of-modern-c-style/

C++11标准提供了许多有用的新特性。这篇文章特别针对使C++11和C++98相比看上去像一门新语言的特性,因为:

  • C++11的这些特性改变了书写C++代码的风格和习惯,也改变了设计C++库的方式。例如,你会看到更多的被当作参数和返回值的智能指针,还有按值(by value)返回超大对象的函数。
  • 它们被使用的非常广泛,在大多数代码中你都能看到它们。举个例子,在现代C++中几乎每5行C++代码你就能看到auto关键字。

还有一些其它的非常好的C++11新特性,但先把这篇文章所描述的新特性熟悉起来把,因为这些被广泛使用的特性展示了为什么C++11代码是简洁的,安全的和快速的,就像其它现代主流开发语言一样,并且性能和传统C++一样强大。

1. Auto

在任何可能的时候使用auto。因为有两个原因。第一,非常明显,能够避免重复输入我们已经声明的并且编译器已经认识的类型名称,这是非常方便的。

 // C++98

 map<int,string>::iterator i = m.begin();

 double const xlimit = config["xlimit"];

 singleton& s = singleton::instance(); 

 // C++11

 auto i = begin(m);

 auto const xlimit = config["xlimit"];

 auto& s = singleton::instance();

第二,当有遇到一个你不知道或者无法用语言表达的类型时,auto就不仅仅是使用方便这么简单了,比如,大多数lambda函数的类型,你不能够容易的将其类型拼写出来甚至根本就不能够写出来。

// C++98

binder2nd< greater > x = bind2nd( greater(),  ); 

// C++11

auto x = [](int i) { return i > ; };

注意,使用auto并没有修改代码的语义。代码仍然是静态类型(statically typed),并且每个表达式都干净利落;只是不再强制我们多余的重新声明类型的名称。

一些人开始的时候害怕使用auto,因为给人的感觉像是并没有声明(重新声明)我们想要的类型,这意味着我们可能会突然得到一个不同的类型。如果你想显示的做强制类型转换,这没有问题;声明目标类型就可以了。但是在大部分情况下,使用auto就足够了,由于出现错误而得到另外一个类型的情况很少见,在使用强静态类型(strong static typing)情况下,如果类型出现错误编译器就会告诉你。

2. 智能指针,no delete

总是使用智能指针,不要用原生指针和delete。除非需要实现你自己的底层数据结构(把原生指针很好的封装在类(class boundary)中

如果你知道你是另外一个对象的唯一拥有着,使用unique_ptr来表示唯一的拥有权。一个"new T"表达式能很快的初始化一个拥有 这个智能指针的对象,特别是unique_ptr。典型的例子是指向实现的指针(Pimpl Idiom):

 // C++11 Pimpl idiom: header file
class widget {
public:
widget();
// ... (see GotW #100) ...
private:
class impl;
unique_ptr<impl> pimpl;
}; // implementation file
class widget::impl { /*...*/ }; widget::widget() : pimpl{ new impl{ /*...*/ } } { }
// ...

使用shared_ptr来表示共享所有权(shared ownership)。使用make_shared来创建共享对象更好。

 // C++98
widget* pw = new widget();
:::
delete pw; // C++11
auto pw = make_shared<widget>();

使用weak_ptr来打破循环和表示可选性(比如实现一个对象缓存)

 // C++11
class gadget; class widget {
private:
shared_ptr<gadget> g; // if shared ownership
}; class gadget {
private:
weak_ptr<widget> w;
};

如果你了解到另外一个对象比你的生存周期要长,并且你想观察这个对象,那么使用原生指针(raw pointer)。

 // C++11
class node {
vector<unique_ptr<node>> children;
node* parent;
public:
:::
};

3. Nullptr

用nullptr来表示一个空指针,不要再使用数字0或者宏NULL来表示空指针了,因为这些是模棱两可的,既能表示整形也可表示指针。

 // C++98
int* p = ; // C++11
int* p = nullptr;

4. Range for

对一个范围内的元素进行有序访问,基于range的for循环会是更方便的用法。

 // C++98
for( vector<int>::iterator i = v.begin(); i != v.end(); ++i ) {
total += *i;
} // C++11
for( auto d : v ) {
total += d;
}

5. 非成员begin和end

使用非成员函数begin(x)和end(x)(不是x.begin()和x.end()),因为begin(x)和end(x)是可扩展的,能同所有容器类型一块工作——甚至数组也可以——并不是只针对提供了STL风格的x.begin()和x.end()成员函数的容器。
如果你正在使用一个非STL集合类型,这个类型提供迭代器但不是STL风格的x.begin()和x.end(),你可以对他的非成员函数begin()和end()进行重载,这样你就可以使用同STL容器同样的风格进行编码。标准中举了一个例子:数组,并且提供了对象的begin和end函数:

 vector<int> v;
int a[]; // C++98
sort( v.begin(), v.end() );
sort( &a[], &a[] + sizeof(a)/sizeof(a[]) ); // C++11
sort( begin(v), end(v) );
sort( begin(a), end(a) );

6. Lambda函数和算法

Lambda表达式改变了游戏规则,它会时不时的改变你的编码方式,这种方式优雅并且快速。Lambda使现存STL算法实用性提高了百倍。
新增加的C++库的设计都以支持lambad表达式为前提(例如:PPL),甚至有一些库需要通过你编写lambda表达式来它(例如:c++ AMP)。
这里有个例子,找到v中的>X并且<Y的第一个元素。在C++11中,最简单并且干净的代码是使用标准算法。

 // C++98: write a naked loop (using std::find_if is impractically difficult)
vector<int>::iterator i = v.begin(); // because we need to use i later
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; } );

想使用一个循环或者类似的语言特性(language feature)但实际上在该语言中并不存在,怎么办?将其实现成模板函数(库算法)就可以了,多亏了lambda,使用它就像是用一个语言特性一样的方便,但是更灵活,因为它确实是一个库而不是一个固定的语言特性。

 // C#
lock( mut_x ) {
... use x ...
} // C++11 without lambdas: already nice, and more flexible (e.g., can use timeouts, other options)
{
lock_guard<mutex> hold { mut_x };
... use x ...
} // C++11 with lambdas, and a helper algorithm: C# syntax in C++
// Algorithm: template<typename T> void lock( T& t, F f ) { lock_guard hold(t); f(); }
lock( mut_x, [&]{
... use x ...
});

熟悉一下lambda吧,你会发现他们很有用,并不只是在c++中,它们已经在几个主流语言中得到支持并且流行开来。

7. Move/&&

把move当作是对拷贝的优化最合适不过了,虽然它也包含其他方面的东西(像完美转发(perfect forwarding))
move语义改变了我们设计API的方式。我们会越来越多的将函数设计成return by value。

 // C++98: alternatives to avoid copying
vector<int>* make_big_vector(); // option 1: return by pointer: no copy, but don't forget to delete
:::
vector<int>* result = make_big_vector(); void make_big_vector( vector<int>& out ); // option 2: pass out by reference: no copy, but caller needs a named object
:::
vector<int> result;
make_big_vector( result ); // C++11: move
vector<int> make_big_vector(); // usually sufficient for 'callee-allocated out' situations
:::
auto result = make_big_vector(); // guaranteed not to copy the vector

如果你想获得比copy更高效的办法,对你的类型使用move语义吧。

8. 统一的初始化和初始化列表

没有发生变化的:当初始化一个non-POD或者auto的本地变量时,继续使用熟悉的不带额外花括号{}的=语法。

 // C++98 or C++11
int a = ; // still fine, as always // C++ 11
auto x = begin(v); // no narrowing or non-initialization is possible

在其他情况中(特别是随处可见的使用()来构造对象),使用花括号{}会更好。使用花括号{}能避免一些潜在的问题:你不会突然得到一个收缩转换(narrowing conversions)后的值(比如,float转换成int),也不会有偶尔突发的未初始化POD成员变量或者数组的存在,也能避免在c++98中会碰到的奇怪事:你的代码编译没问题,你需要的是变量但实际上你声明了一个函数,这都源于C++声明语法的模糊不清,Scott Meyers的著名说法:“C++最令人苦恼的解析”。通过使用新风格的语法上面解析问题会不复存在。

 // C++98
rectangle w( origin(), extents() ); // oops, declares a function, if origin and extents are types
complex<double> c( 2.71828, 3.14159 );
int a[] = { , , , };
vector<int> v;
for( int i = ; i <= ; ++i ) v.push_back(i); // C++11
rectangle w { origin(), extents() };
complex<double> c { 2.71828, 3.14159 };
int a[] { , , , };
vector<int> v { , , , };

新的{}语法在几乎任何地方都能出色的工作。

 // C++98
X::X( /*...*/ ) : mem1(init1), mem2(init2, init3) { /*...*/ } // C++11
X::X( /*...*/ ) : mem1{init1}, mem2{init2, init3} { /*...*/ }

最后,有时候传递不带(type-named temporary)的函数参数是很方便的:
void draw_rect( rectangle );

 // C++98
draw_rect( rectangle( myobj.origin, selection.extents ) ); // C++11
draw_rect( { myobj.origin, selection.extents } );

我不喜欢使用花括号{}的唯一地方是在初始化一个非POD变量的时候,像 auto x= begin(v);使用花括号会使代码不必要的丑陋,因为我知道了它是一个类类型,所以我不必担心收缩转换,并且现代编译器已经对额外的拷贝(或者额外move,如果类型是move-enabled)进行了优化。

C++ 11和C++98相比有哪些新特性的更多相关文章

  1. Java 11正式发布,这几个逆天新特性教你写出更牛逼的代码

    就在前段时间,Oracle 官方宣布 Java 11 (18.9 LTS) 正式发布,可在生产环境中使用! 这无疑对我们来说是一大好的消息.作为一名java开发者来说,虽然又要去学习和了解java11 ...

  2. Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码

    美国时间 09 月 25 日,Oralce 正式发布了 Java 11,这是据 Java 8 以后支持的首个长期版本. 为什么说是长期版本,看下面的官方发布的支持路线图表. 可以看出 Java 8 扩 ...

  3. Struts 2 框架相比Struts1的新特性

    POJO形式和POJO动作 - 已经摆脱了Struts2的动作表单的Struts框架的一个组成部分.Struts2可以使用任何的POJO接收的形式输入.同样的,你现在可以看到任何POJO的Action ...

  4. 【转】C++11 标准新特性: 右值引用与转移语义

    VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...

  5. C++11 标准新特性: 右值引用与转移语义

    文章出处:https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/ 新特性的目的 右值引用 (Rvalue Referene) ...

  6. Java知识回顾 (18)Java 8、9、11的新特性

    Java 8 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本. Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 Jav ...

  7. 在C++98基础上学习C++11新特性

    自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...

  8. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  9. C++11新特性总结 (一)

    1. 概述 最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己写了,看别人写的代码估计都会有些吃力.C++ Primer5是学习C++1 ...

随机推荐

  1. webstorm license key

    JetBrains WebStorm注册码 UserName: William License Key : ===== LICENSE BEGIN ===== 45550-12042010 00001 ...

  2. ps你最容易忽略的知识

    了解更多ps知识 1. 快速打开文件­ 双击Photoshop的背景空白处(默认为灰色显示区域)即可打开选择文件的浏览窗口.­ 2. 随意更换画布颜色­ 选择油漆桶工具并按住Shift点击画布边缘,即 ...

  3. (原创)项目部署-Tomcat设置默认访问项目及项目重复加载问题处理

    主要是通过配置<Tomcat安装目录>/conf/server.xml文件 步骤: 1.打开server.xml,在</Host>的上一行添加内容格式如下 <Contex ...

  4. Java开发常用Linux命令

    1.查找文件 find / -name filename.txt根据名称查找/目录下的filename.txt文件. find . -name "*.xml"递归查找所有的xml文 ...

  5. case语句

    case语句是多分支选择语句,if语句只有两个分支可供选择,而实际问题中常常需要用到多分支选择结构.例如,学生成绩分类(90分以上为A,--):人口统计分类(按年龄分为老.中.青.少.幼)等.当然这些 ...

  6. Helpers\Document

    Helpers\Document The document class is a collection of useful methods for working with files. To get ...

  7. php设计模式-------(1)策略模式

    一.为什么我要学习设计模式. 我的上一个项目是做App接口,由于时间紧,老板催的急,所以到最后项目完工时发现居然写了几万行代码,可想而知代码质量有多糟糕.而且很多时候,调用接口的开发人员来找我说某个接 ...

  8. 映像备份与恢复管理工具Easy Image X使用说明

    Easy Image X(简称EIX)是一个支持Ghost映像(.gho)和ImageX映像(.wim)的映像管理工具,具有友好的图形界面,仅需几步简单操作即可完成映像备份与恢复工作.维护时使用最多的 ...

  9. JS类型判定方法(不包括自定义类型)

    //判定数据类型 function isType(obj, type) { return toString.call(obj).indexOf('[object ' + type) == 0; } / ...

  10. C++ ofstream和ifstream详细用法

    转载地址:http://soft.chinabyte.com/database/460/11433960.shtml ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就 ...