C++11常用特性学习-保证稳定(_func_/long long/static_assert/final/override)

来源:互联网 发布:印刷厂软件 编辑:程序博客网 时间:2024/06/10 02:18

C++11常用特性学习

C++11标准为C++编程语言的第三个官方标准,正式名叫ISO/IEC 14882:2011 - Information technology – Programming languages – C++。 在正式标准发布前,原名C++0x。2014年8月18日,经过C++标准委员投票,C++14标准获得一致通过。目前C++17已经发布指导文件,增加了许多语言特性,进一步完善C++语言支持和特性(作死的道路越走越远,规范的API,统一类库和编译特性都木有啊);
学习要了解的有:

  1. 新的算法和类库;
  2. auto和decltype类型推导的关系;
  3. 移动语义,以及右值引用是如何解决转发问题;
  4. default/delete函数以及override是怎么回事;
  5. 异常描述符和noexcept;
  6. 原子类型和新的内存模型;
  7. C++11并行编程;

主要参考:《深入理解C++11新特性解析与应用》以及随笔分类 - C++11学习和每个C++开发者都应该使用的十个C++11特性、开发者都应该使用的10个C++11特性、C++11新特性等博客。本文只学习C11常用并且在VS,GCC和Clang等编译器都已经较好的支持并且常用和易于理解的部分功能。

  • C11常用特性学习
    • 编译器的选择
    • 保证稳定和兼容的新特性
      • _func_预定义标识符
      • long long整型
      • 断言运行时与预处理时
      • finaloverride控制

编译器的选择

C11准于2011年发布并出版,出版时,主流编译器并不支持C11的所有特性,目前(2015年11月)公司主流项目使用的编译器VS12 (Visual Studio 2012)、g++ 4.7和Clang 3.1对C++的支持力度各部相同;但对常用的auto等常用功能都有了支持,具体支持情况见:C++11各编译器支持情况对比;目前VS13(Visual Studio 2013)、gcc 4.8.1已经支持C11大部分特性。VS15(Visual Studio 2015)支持 C++11/14/17 功能(现代 C++)和GCC 5.2 已经开始支持C17功能。可以选用Vs2013和gcc4.8.1来学习。(后记:MD,VS2013 C11预览版BUG满天飞,VS15版安装包5个G,搞死人啊!!)

C++之父Bjame Stroustrup说C11就像一个新语言,的确,C11核心已经发生了巨大的变化,它现在支持Lambda表达式,对象类型自动推断,统一的初始化语法,委托构造函数,deleted和defaulted函数声明nullptr,以及最重要的右值引用。C11中值得关注的几大变化:

◆ Lambda表达式
◆自动类型推断和decltype
◆统一初始化语法
◆ Deleted和Defaulted函数
◆ nullptr
◆ 委托构造函数
◆ 右值引用

上述特性在文章:C++11它就像一个新语言、掀起C++ 11的神秘面纱提供的连接中有详细的探讨。相对于C++98/03来说,主要是为了增强和优化如下几点:
◆通过内存模型、线程、原子操作等支持本地并行编程(Native Concurrentcy);
◆通过统一初始化表达式、auto、declytype、移动语义等来统一对泛型编程的支持;
◆通过constexpr、POD(概念)等更好支持系统编程;
◆通过内联命名空间、继承构造函数和右值引用等、以更好的支持库的构建。

注:通过以上辛苦的工作,C11朝着“恐怖编程语言泛型”前进;

保证稳定和兼容的新特性

_func_预定义标识符

func预定义标识符表示函数的名字;

#include <string>#include <iostream>using namespace std;const char* hello() { return __func__; }const char* world() { return __func__; }int main(){    cout << hello() << ", " << world() << endl; // hello, world}

我们定义了两个函数hello和world。利用func预定义标识符,我们返回了函数的名字,并将其打印出来。事实上,按照标准定义,编译器会隐式地在函数的定义之后定义func标识符。
void FuncFail( string func_name = func) {};// 无法通过编译
这是由于在参数声明时,func还未被定义。

long long整型

long long整型有两种:long long和unsigned long long。在C++11中,标准要求long long整型可以在不同平台上有不同的长度,但至少有64位。我们在写常数字面量时,可以使用LL后缀(或是ll)标识一个long long类型的字面量,而ULL(或ull、Ull、uLL)表示一个unsigned long long类型的字面量。比如:
long long int lli = -9000000000000000000LL;
unsigned long long int ulli = -9000000000000000000ULL;
与long long整型相关的一共有3个:LLONG_MIN、LLONG_MAX和ULLONG_MIN,它们分别代表了平台上最小的long long值、最大的long long值,以及最大的unsigned long long值。

#include <climits>#include <cstdio>using namespace std;int main() {    long long ll_min = LLONG_MIN;    long long ll_max = LLONG_MAX;    unsigned long long ull_max = ULLONG_MAX;    printf("min of long long: %lld\n", ll_min); // min of long long: -9223372036854775808    printf("max of long long: %lld\n", ll_max); // max of long long: 9223372036854775807    printf("max of unsigned long long: %llu\n", ull_max);   // max of unsigned long long: 18446744073709551615}

断言:运行时与预处理时

在C++中,程序员也可以定义宏NDEBUG来禁用assert宏。

#include <cassert>using namespace std;// 一个简单的堆内存数组分配函数char * ArrayAlloc(int n) {    assert(n > 0);  // 断言,n必须大于0    return new char [n];}int main (){    char* a = ArrayAlloc(0);} 

这对发布程序来说还是必要的。因为程序用户对程序退出总是敏感的,而且部分的程序错误也未必会导致程序全部功能失效。那么通过定义NDEBUG宏发布程序就可以尽量避免程序退出的状况。而当程序有问题时,通过没有定义宏NDEBUG的版本,程序员则可以比较容易地找到出问题的位置。事实上,assert宏在中的实现方式类似于下列形式:

#ifdef  NDEBUG# define assert(expr)           (static_cast<void> (0))#else...#endif

可以看到,一旦定义了NDBUG宏,assert宏将被展开为一条无意义的C语句(通常会被编译器优化掉)。但是assert是在运行时进行断言,我们希望在有些时候编译的过程中就能发现某些问题,从而方便排错。

断言assert宏只有在程序运行时才能起作用。而#error只在编译器预处理时才能起作用。有的时候,我们希望在编译时能做一些断言。在一些C++的模板的编写中,我们希望对参数进行初步判断,如保证两种类型的长度一致,这样bit_copy才能够保证复制操作不会遇到越界等问题。这种情况下,正确产生断言的时机应该在模板实例化时,即编译时期。事实上,利用语言规则实现静态断言的讨论非常多,比较典型的实现是开源库Boost内置的BOOST_STATIC_ASSERT断言机制(利用sizeof操作符)。
在C++11标准中,引入了static_assert断言来解决这个问题。static_assert使用起来非常简单,它接收两个参数,一个是断言表达式,这个表达式通常需要返回一个bool值;一个则是警告信息,它通常也就是一段字符串。如:

template <typename t, typename u> int bit_copy(t& a, u& b){     static_assert(sizeof(b) == sizeof(a),"the parameters of bit_copy must have same width.");};

这样的错误信息就非常清楚,也非常有利于程序员排错。而由于static_assert是编译时期的断言,其使用范围不像assert一样受到限制。

final/override控制

Java语言使用了final关键字来阻止函数继续重写。final关键字的作用是使派生类不可覆盖它所修饰的虚函数。C11也采用了类似的做法,

struct Object{    virtual void fun() = 0;};struct Base : public Object {    void fun() final;   // 声明为final};struct Derived : public Base {    void fun();     // 无法通过编译};// 编译选项:g++ -c -std=c++11 2-10-2.cpp

在C++11中为了帮助程序员写继承结构复杂的类型,引入了虚函数描述符override,如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译。防止在子类重载时出现参数和拼写错误。

0 0