C++ 11和C++98相比有哪些新特性

小编 2026-06-22 阅读:1973 评论:0
 此文是如下博文的翻译:https://herbsutter.com/elements-of-m...

 此文是如下博文的翻译:

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。因为有两个原因。第一,非常明显,能够避免重复输入我们已经声明的并且编译器已经认识的类型名称,这是非常方便的。

 1 // C++98 2  3 map<int,string>::iterator i = m.begin(); 4  5 double const xlimit = config["xlimit"]; 6  7 singleton& s = singleton::instance();  8  9 // C++1110 11 auto i = begin(m);12 13 auto const xlimit = config["xlimit"];14 15 auto& s = singleton::instance();

 

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

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

 

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

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

2. 智能指针,no delete

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

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

 1 // C++11 Pimpl idiom: header file 2 class widget { 3 public: 4     widget(); 5     // ... (see GotW #100) ... 6 private: 7     class impl; 8     unique_ptr<impl> pimpl; 9 };10  11 // implementation file12 class widget::impl { /*...*/ };13  14 widget::widget() : pimpl{ new impl{ /*...*/ } } { }15 // ...

 

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

1 // C++982 widget* pw = new widget();3 :::4 delete pw;5  6 // C++117 auto pw = make_shared<widget>();

 



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

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

 


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

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

 

3. Nullptr


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

1 // C++982 int* p = 0;3  4 // C++115 int* p = nullptr;

 

4. Range for


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

1 // C++982 for( vector<int>::iterator i = v.begin(); i != v.end(); ++i ) {3     total += *i;4 }5  6 // C++117 for( auto d : v ) {8     total += d;9 }

 

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函数:

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

 

6. Lambda函数和算法

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

1 // C++98: write a naked loop (using std::find_if is impractically difficult)2 vector<int>::iterator i = v.begin(); // because we need to use i later3 for( ; i != v.end(); ++i ) {4     if( *i > x && *i < y ) break;5 }6  7 // C++11: use std::find_if8 auto i = find_if( begin(v), end(v), [=](int i) { return i > x && i < y; } );

 

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

 1 // C# 2 lock( mut_x ) { 3     ... use x ... 4 } 5   6 // C++11 without lambdas: already nice, and more flexible (e.g., can use timeouts, other options) 7 { 8     lock_guard<mutex> hold { mut_x }; 9     ... use x ...10 }11  12 // C++11 with lambdas, and a helper algorithm: C# syntax in C++13 // Algorithm: template<typename T> void lock( T& t, F f ) { lock_guard hold(t); f(); }14 lock( mut_x, [&]{15     ... use x ...16 });

 

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

7. Move/&&


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

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

 

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

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


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

1 // C++98 or C++112 int a = 42;        // still fine, as always3  4 // C++ 115 auto x = begin(v); // no narrowing or non-initialization is possible

 

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

 1 // C++98 2 rectangle       w( origin(), extents() );   // oops, declares a function, if origin and extents are types 3 complex<double> c( 2.71828, 3.14159 ); 4 int             a[] = { 1, 2, 3, 4 }; 5 vector<int>     v; 6 for( int i = 1; i <= 4; ++i ) v.push_back(i); 7   8 // C++11 9 rectangle       w   { origin(), extents() };10 complex<double> c   { 2.71828, 3.14159 };11 int             a[] { 1, 2, 3, 4 };12 vector<int>     v   { 1, 2, 3, 4 };

 

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

1 // C++982 X::X( /*...*/ ) : mem1(init1), mem2(init2, init3) { /*...*/ }3  4 // C++115 X::X( /*...*/ ) : mem1{init1}, mem2{init2, init3} { /*...*/ }

 

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

1 // C++982 draw_rect( rectangle( myobj.origin, selection.extents ) );3  4 // C++115 draw_rect( { myobj.origin, selection.extents } );

 

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


 


作者:HarlanC

博客地址:http://www.cnblogs.com/harlanc/
个人博客: http://www.harlancn.me/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接

如果觉的博主写的可以,收到您的赞会是很大的动力,如果您觉的不好,您可以投反对票,但麻烦您留言写下问题在哪里,这样才能共同进步。谢谢!

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表