码迷,mamicode.com
首页 > 编程语言 > 详细

C++ 11新特性解析——《深入理解C++ 11:C++11新特性解析和应用》读书笔记

时间:2016-08-14 07:31:08      阅读:249      评论:0      收藏:0      [点我收藏+]

标签:

  因为偶然的机会,在图书馆看到《深入理解C++ 11:C++11新特性解析和应用》这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出这篇读书笔记的基础。C++作为踏入编程的最初语言,一直充满感情,而C++11作为新标准虽然推出一段时间了,却因为总总原因直到现在才去开始真正了解,不过一句话回荡在脑中:当你认为为时已晚的时候,恰恰是最早的时候!从C++98到C++11, C++11标准经历了10几年的沉淀,以全新的姿态迎接新的挑战,长话短说,进入正题,来一起了解吧。(注:本笔记中所有代码在Code::Blocks下编辑,使用gcc-4.9.3 -std=c++11模式下编译)

C++11的新基础特性

  1.1 用于兼容C99特性的宏,可以检查编译系统对标准C库的支持情况,不过测试部分显示未定义。

    cout<<"Standard Clib: "<< __STDC_HOSTED__<<endl;        //Standard Clib: 1(指定编译器目标系统是否包含完整的C库)
    cout<<"Standard C: "<<__STDC__<<endl;                   //__STDC__:1(指定编译器目标系统是否与C标准一致)
    //cout<<"C Standard Version: "<<__STDC_VERSION__<<endl; //测试gcc 没有定义(支持标准C库的版本)
    //cout<< "ISO/IEC: "<<__STDC_ISO_10646__<<endl;         //测试gcc 没有定义

  1.2 __func__ 用于获得当前函数名字符串的宏

const char * Stanard_Macros(void)
{
    //......       
    return __func__; //返回值:Stanard_Macros   
}    

  1.3 _Pragma 预处理操作符,与#pragma功能相同,不过因为支持传递字符串,所以可以用宏命令替代,如对于经常使用的头文件单次包含。

//头文件防止重复包含
#pragma once;

_Pragma("once");    

#define PRAGMA(x) _Pragma(#x)    //宏命令中使用#可以实现替换字符串化
PRAGMA(once);

   1.4 __VA_ARGS__ 变长参数的宏定义是指宏定义中参数列表的最后一个参数为..., 而实现部分可以用__VA_ARGS__替换

//__FILE__当前文件路径
//__LINE__当前文本行
#define LOG(...){ \
    fprintf(stderr, "%s: Line %d:\t", __FILE__, __LINE__);\     //输出错误的文件及行号地址
    fprintf(stderr, __VA_ARGS__);\                     //输出错误的数据
    fprintf(stderr, "\n");  \    
}
           
LOG("%s", "NO ERR!"); // xxxx:xxxx: NO ERR!

  1.5 新的整型long long/unsigned long long(长度不小于64位)

    long long int lli = -90000000000LL;
    unsigned long long int ulli = 9000000000000ULL;
    cout<<"LLONG_MIN: "<<LLONG_MIN<<endl;                   //LLONG_MIN: -9223372036854775808
    cout<<"LLONG_MAX: "<<LLONG_MAX<<endl;                   //LLONG_MAX: 9223372036854775807
    cout<<"ULLONG_MAX: "<<ULLONG_MAX<<endl;                 //ULLONG_MAX: 18446744073709551615

  1.6 断言帮助开发者快速定位问题违反程序前提条件的错误,不过断言只在程序运行时执行,这在某些情况下,是不可接受的,特别是对于模板实例化时出现的错误,应该在编译器就确定。在C++11中引入了static_assert断言来解决问题,它支持两个参数输入,一个是返回bool型的表达式,另一个是警告信息。

static_assert(sizeof(b)==sizeof(a), "the parameters of bit_copy must have same width"); //静态断言,编译器确定,返回false则触发告警信息

  1.7 noexcept修饰符和noexcept操纵符是提供给库作者使用,表示函数如果出现异常,不会抛出,编译器会直接调用std::terminate()函数来终止程序运行,从而阻止了异常的的传播和扩散。此外:C++11析构函数默认是noexcept(true),如果希望抛出异常,需要自己声明noexcept(false)(来自书上,不过自我测试gcc不加析构函数也会有异常抛出)。

const char * Stanard_Macros(void) noexcept     //相当于noexcept(true), 不抛出异常

const char * Stanard_Macros(void) noexcept(true) //noexcept中可以为结果为bool型的表达式,如果为false,则异常正常抛出,true则不抛出异常

  1.8 类成员变量的快速初始化新的列表初始化,在C++11中,除了静态变量,对于其它变量也允许使用等号或{}进行就地初始化。

class Mem{
public:
    Mem(int i): m(i){};
        ~Mem(){};
    const char *ShowMem(void){  std::cout<<"Mem: "<<m<<" ";
                                return __func__;
                            };
private:
    int m{0};
};

//快速初始化
//初始化列表优于就地的列表初始化
class Init{
public:
    Init(): a(0){};       Init(int d): val(G), a(d){}; //初始化列表实际效果后作用,效果上优于列表初始化
//      ~Init(){ throw 1; };
        ~Init(){};
    const char * showPara(std::string str);
    int a;
private:
    char val{g};             //成员变量的快速初始化 
    Mem mem{1};
    std::string name{"Init"};
};

//C++的成员变量快速初始化
Init init1{5};                //C++11的统一列表初始化
Init init2;
init1.showPara("init1: ");    //init1: 5 init1: G name :Init Mem: 1 mem :ShowMem
init2.showPara("init2: ");    //init2: 0 init2: g name :Init Mem: 1 mem :ShowMem        

  1.9 非静态成员的sizeof, sizeof作为运算符,对于处理数组,类和对象时经常用到,不过在之前的C++98中,对于非静态成员是不能直接编译的,需要借用对像实例。

    cout<<"Dyna Sizeof: "<<sizeof(((Init *)0)->a)<<endl;    //C++98时借用实例对象获得非静态成员长度
    cout<<"Dyna Sizeof: "<<sizeof(Init::a)<<endl;           //C++11支持直接获得

    1.10 扩展的friend用法, friend时C++中比较特别的关键字,一方面它让程序员省下了很多代码,另一方面也破坏了OOP中的封装性,在C++11中做了改进,以保证更好的运用。

class Ploy;
typedef Ploy P;

//friend支持别名, 不带class以及类模板声明友元
template<typename T = int>
class People{
    friend T;
};

People<P> PP;   //类型P(Ploy的别名)声明为PP的友元
People<int> Pi; //对于int类型参数模板,友元声明被忽略

   1.11 finaloverride控制, final用来限制基类虚函数的对应的派生类不能重写该虚函数,从而避免了某些接口被重写覆盖; override则指定了函数必须重载基类的虚函数,否则编译通不过,这就避免了某些输入名或者原型不匹配等错误的发生。

class MathObject{
public:
    virtual double Arith() = 0;
    virtual void Print() = 0;
};

class Printable:public MathObject{
public:
    double Arith() = 0;         //纯虚函数仅允许为0
    void Print() final{    
        std::cout<<"Output is: "<< Arith() <<std::endl;
    }
};

class Add2:public Printable{
public:
    Add2(double a, double b):x(a), y(b){}
    double Arith() override{    //override指定函数为重载函数,进行检查(对于一定重载函数检查,避免了输入错误
        return x+y;
    }
//    void Print(){}            //编译会报错,因为父类声明了final,子类不允许重载
private:
    double x, y;
}

  1.12 默认的模板参数,C++11中模板和函数一样,支持默认参数。

//类模板 C++98就允许,不过定义有要求
template<typename T1, typename T2 = int>class DefClass1{};      //允许,指定类模板的默认模板参数
//template<typename T1 = int, typename T2> class DefClass2;     //通不过编译,多个默认模板参数指定默认值时,必须遵守从右向做的原则

//函数模板 C++11添加
template<typename T1, typename T2 = int>void DefFunc1(T1 a, T2 b);
template<typename T1 = int, typename T2>void DefFunc2(T1 a, T2 b); //允许

   1.13 外部模板

  外部模板实现依赖于C++98已有的特性,显示实例化。这样就可以实现一次实例,多次使用。

template <typename T>void fun(T) {}    //模板函数定义
template void fun<int>(int);           //模板实例化声明
extern template void fun<int>(int);    //模板外部声明

   1.14 局部或者匿名类型做模板实参

   C++11支持匿名或者局部类型作为模板的实参,提供了更多的使用方法。

//2.13 局部和匿名类型作函数模板实参
template<typename T> class X{};
template<typename T> void TempFunc(T t){};
struct {int i;}b;
typedef struct{int i;}B;

struct C{} c;
X<B> x1;       //匿名结构体作为实参,不过只支持别名,不支持匿名结构体直接作为实参
X<C> x2;       //局部变量作为实参   
TempFunc(b);    //匿名类型变量,C++11允许
TempFunc(c);     //局部类型变量,C++11允许

  到现在为止,C++11的新基础特性中比较重要的部分差不多讲完了,从这些改动可以看出,C++11向着更方便,更强大的方向稳步前进,而且这些改动只是沧海一粟,如新的lambda表达式,类的构造函数的新实现,更加常态化的SFINAE,这些都值得研读,不过今天有点晚了,先到此为止,后面我会一边测试一边总结,感谢C++11委员会,也感谢本书作者详细的阐述,受用无穷!

C++ 11新特性解析——《深入理解C++ 11:C++11新特性解析和应用》读书笔记

标签:

原文地址:http://www.cnblogs.com/zc110747/p/5761037.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!