网站首页 > java教程 正文
在C++11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛。不过我相信这些新特性当中有一些,应该成为所有C++开发者的常规装备。你也许看到过许多类似介绍各种C++11特性的文章。下面是我总结的,C++开发者都需要学习和使用的C++11新特性。
auto
在C++11之前,auto关键字用来指定存储期。在新标准中,它的功能变为类型推断。auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。各种作用域内声明变量都可以用到它。例如,名空间中,程序块中,或是for循环的初始化语句中。
123 | auto i = 42; // i is an intauto l = 42LL; // l is an long longauto p = newfoo(); // p is a foo* |
使用auto通常意味着更短的代码(除非你所用类型是int,它会比auto少一个字母)。试想一下当你遍历STL容器时需要声明的那些迭代器(iterator)。现在不需要去声明那些typedef就可以得到简洁的代码了。
1234 | std::map<std::string, std::vector<int>> map;for(auto it = begin(map); it != end(map); ++it){} |
需要注意的是,auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数的返回值类型就是operator+操作符作用在T1、T2类型变量上的返回值类型。
123456 | template<typenameT1, typenameT2>auto compose(T1 t1, T2 t2) -> decltype(t1 + t2){returnt1+t2;}auto v = compose(2, 3.14); // v's type is double |
nullptr
以前都是用0来表示空指针的,但由于0可以被隐式类型转换为整形,这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值,用来指代空指针。nullptr和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为bool型(取值为false)。但是不存在到整形的隐式类型转换。
123456789101112131415 | voidfoo(int* p) {}voidbar(std::shared_ptr<int> p) {}int* p1 = NULL;int* p2 = nullptr;if(p1 == p2){}foo(nullptr);bar(nullptr);boolf = nullptr;inti = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type |
为了向前兼容,0仍然是个合法的空指针值。
Range-based for loops(基于范围的for循环)
为了在遍历容器时支持”foreach”用法,C++11扩展了for语句的语法。用这个新的写法,可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。
如果你只是想对集合或数组的每个元素做一些操作,而不关心下标、迭代器位置或者元素个数,那么这种foreach的for循环将会非常有用。
12345678910111213141516171819202122 | std::map<std::string, std::vector<int>> map;std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);map["one"] = v;for(constauto& kvp : map){std::cout << kvp.first << std::endl;for(auto v : kvp.second){std::cout << v << std::endl;}}intarr[] = {1,2,3,4,5};for(int& e : arr){e = e*e;} |
Override和final
我总觉得 C++中虚函数的设计很差劲,因为时至今日仍然没有一个强制的机制来标识虚函数会在派生类里被改写。vitual关键字是可选的,这使得阅读代码变得很费劲。因为可能需要追溯到继承体系的源头才能确定某个方法是否是虚函数。为了增加可读性,我总是在派生类里也写上virtual关键字,并且也鼓励大家都这么做。即使这样,仍然会产生一些微妙的错误。看下面这个例子:
1234567891011 | classB{public:virtualvoidf(short) {std::cout << "B::f"<< std::endl;}};classD : publicB{public:virtualvoidf(int) {std::cout << "D::f"<< std::endl;}}; |
D::f 按理应当重写 B::f。然而二者的声明是不同的,一个参数是short,另一个是int。因此D::f(原文为B::f,可能是作者笔误——译者注)只是拥有同样名字的另一个函数(重载)而不是重写。当你通过B类型的指针调用f()可能会期望打印出D::f,但实际上则会打出 B::f 。
另一个很微妙的错误情况:参数相同,但是基类的函数是const的,派生类的函数却不是。
1234567891011 | classB{public:virtualvoidf(int) const{std::cout << "B::f "<< std::endl;}};classD : publicB{public:virtualvoidf(int) {std::cout << "D::f"<< std::endl;}}; |
同样,这两个函数是重载而不是重写,所以你通过B类型指针调用f()将打印B::f,而不是D::f。
幸运的是,现在有一种方式能描述你的意图。新标准加入了两个新的标识符(不是关键字)::
1. override,表示函数应当重写基类中的虚函数。
2. final,表示派生类不应当重写这个虚函数。
第一个的例子如下:
1234567891011 | classB{public:virtualvoidf(short) {std::cout << "B::f"<< std::endl;}};classD : publicB{public:virtualvoidf(int) override {std::cout << "D::f"<< std::endl;}}; |
现在这将触发一个编译错误(后面那个例子,如果也写上override标识,会得到相同的错误提示):
1 | 'D::f' : method with override specifier 'override' did not override any base class methods |
另一方面,如果你希望函数不要再被派生类进一步重写,你可以把它标识为final。可以在基类或任何派生类中使用final。在派生类中,可以同时使用override和final标识。
1234567891011121314151617 | classB{public:virtualvoidf(int) {std::cout << "B::f"<< std::endl;}};classD : publicB{public:virtualvoidf(int) override final {std::cout << "D::f"<< std::endl;}};classF : publicD{public:virtualvoidf(int) override {std::cout << "F::f"<< std::endl;}}; |
被标记成final的函数将不能再被F::f重写。
感谢作者冯上(@治不好你我就不是兽医 ),本文转自伯乐在线
大家如果对编程感兴趣,想了解更多的编程知识,解决编程问题,我们这里有java高手,C++/C高手,windows/Linux高手等,大家关注我的微信公众号:程序员互动联盟or coder_online
- 上一篇: 数组实现队列和循环队列(Java)
- 下一篇: Java小技巧:巧用函数方法实现二维数组遍历
猜你喜欢
- 2024-11-21 教你如何从性能角度选择数组的遍历方式
- 2024-11-21 javaScript数组的遍历操作
- 2024-11-21 全新Java入门到架构师教程之Java15数组案例实现和Arrays
- 2024-11-21 第9天|Java入门有野,数组
- 2024-11-21 java数组例子
- 2024-11-21 Java小技巧:巧用函数方法实现二维数组遍历
- 2024-11-21 数组实现队列和循环队列(Java)
- 2024-11-21 java 基础之循环动态接收数组和动态输出数组
- 2024-11-21 带你从零学大数据系列之Java篇---第五章:数组
- 2024-11-21 js forEach修改不了数组,如何遍历数组并修改当前数组
你 发表评论:
欢迎- 最近发表
-
- Java内存溢出紧急处理:10个必知的Linux命令快速定位OOM
- 面试常问的 25+ 个 Linux 命令(linux面试命令大全)
- Java堆外内存溢出紧急处理实战:Linux命令定位与Spring Boot解决
- java开发常用的Linux命令,高频的没你想象的多
- Java 应用 CPU 飙升?8 个 Linux 命令组合拳快速锁定异常线程
- Java 开发者线上问题排查常用的 15 个 Linux 命令
- Java程序员必备的Linux命令:让你的工作效率翻倍
- Java程序员必备的Linux命令全解析
- [超全整理] Java 程序员必备的 100 条 Linux 命令大全
- SAP ABAP资源导航(sap aatp)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)