<C++11 新特性解析与应用> 读书笔记

有趣的知识点

  • __cplusplus

__cplusplus 其实被定义为一个整数 , C++03 中被定义为199711L , C++11 中被定义为201103L .

可以通过下面的代码检测编译器是否支持C++11 .

#if __cplusplus < 201103L
    #error "Should use C++11 implementation" 
#endif
  • 枚举

枚举的作用域即是它所在的namespace, 有是他的父 作用域 。

例子 :

enum Type { General , Light, Medium , Heavy };

Type t1 = General; // Correct 
Type t2 = Type::General ; // Also correct 

实用的改进Feature

  • sizeof

C++98 的sizeof不能使用非static的类成员作为参数, C++ 11 则可以 .

例子 :

class Test
{
    public:
        int m_int;
        static int s_int;
};

#include <iostream>
using std::cout;
using std::endl;
int main()
{
    cout<<sizeof(Test::s_int)<<endl; // both pass
    cout<<sizeof(Test::m_int)<<endl; // C++98 error , C++11 pass
    return 0;
}
  • friend

C++11的friend 关键字不必再写class .
支持模板作为friend. 如果模板的实例化是一个基本类型, 那么就忽略这个友元.

例子

template<typename T>
class Test 
{
    public:
        friend T; // C98 需要写出 friend class T , 且 T 需要为具体的类名 .
    private:
        int a;
};

class FriendClass ;
typedef TestNoFriend Test<int> ;
typedef TestHasFriend Test<FriendClass>;
  • final

对于虚函数(非纯虚) , 使用final关键字可以阻止派生类重载继续这个接口.

例子:

#include <iostream>
using std::cout;
using std::endl;
class GrandPa
{
    public:
        virtual void  Print() = 0;
};

class Father : public GrandPa
{
    public:
        virtual void Print() final { cout<<"Father is always correct ! "<<endl;}
};

class Son : public Father
{
    public: 
        void Print() { /* TODO  want son want*/} //error: overriding final function ‘virtual void Father::Print()’
};
  • override

使用override关键字修饰函数, 指明这个函数是重载了父类的接口, 如果不是,则会编译出错 .

例子:

class Father
{
    public:
        virtual void A(){}
};

class Son : public Father
{
    public:
        virtual void AA() override {} //error: ‘virtual void Son::AA()’ marked override, but does not override
};
  • 模板
  1. 支持带有默认 值/类型 的函数模板.
  2. 支持外部(extern) 模板
  3. 支持局部/匿名类型作为模板实参, 但是这个局部/匿名类型的声明不能出现在模板实参位置.

具体例子请自习查阅书籍.

  • 构造函数
  1. 继承构造函数的
    用途:
    1.1.1 父类的构造函数众多
    1.1.2 派生类添加的数据项较少, 可以通过指定默认值(C++11) 的方式初始化
    注意:
    1.2.1 多重继承的时候注意构造函数冲突问题
  2. 委派构造函数
    用途
    2.1.1 各种构造函数有部分相同的工作
    2.1.2 在初始化列表中使用, 类似父类构造函数的使用
    注意:
    2.3 注意不应该产生环状依赖

例子 :

#include <iostream>
using std::cout;
using std::endl;
class Father
{
    public:
        Father() : Father(1) {} // 委派给只有一个参数的构造函数 .
        Father(int i) : Father(i,2){} // 委派给最详细赋值的构造函数 .
        Father(int i, int j) : a(i) , b(j){} 
        void Print() {cout<<"a :"<<a<<"  b : "<<b<<endl; }
    private: 
        int a;
        int b;  
};

class Son : public Father
{
    public:
            using Father::Father; // 继承父类的构造函数
            void Print() 
            {
                Father::Print();
                cout<<"c :"<<c<<endl;
            }
    private: 
        int c = 3 ; // C++11  可以直接对非static成员进行初始化.
};

int main()
{
    Son a;
    Son b(3);
    Son c(3,4);
    a.Print();
    b.Print();
    c.Print();
    return 0;
}
  • 初始化列表
    • 便捷初始化
            // C++98 error , C++11 pass 
         int a[] = { 1 , 2, 3} ;
         int b[] { 4, 5, 6} ;
         vector<int> c(1,2,3); 

    • 防止类型收窄
int i = 1024;
int j = 10 ;
char  c = {i}; // error , narrowing and miss data
char  c = {j}; // pass , narrowing but no data miss.

新的Feature

右值 , 右值引用 , 移动语义 , std::move
  • 值的分类

    • 左值
    • 右值
      • 纯右值 ( 临时变量 , 字面值 …. )
      • 将亡值 ( std::move 返回值 , T && 函数 / 类型转化 返回值 … )
  • 右值引用 T &&

  • 移动构造函数 T( T && )

移动语义在需要深层拷贝的类有巨大作用(不必重新开辟内存).
理论上 T( const T && ) 是可以的 , 但是没有意义, 移动语义决定了我们应该去改变这个右值 .

  • std::move 将一个左值转化为右值 .

std::move(a) 返回a转化为的右值 , 但是a 本身并没有消亡, a 变量被移动函数改变过之后, 或许数据已经混乱, 不应该继续作为普通左值使用.

  • 利用引用折叠语义, 实现模板的参数可以接受各种类型的数据 , 实现完美转发
template< class T> 
void IamForwarding< T && t>
{
    //IrunCodeActually(static_cast<T && t> );
    IrunCodeActually(std::forward(T && t> );
    /* 
    真正使用的时候, 若T是 A & , 则 && 折叠 , 参数是左值.
                  若T是 A &&, 则 && 折叠 , 参数是右值.
    */
}
自定义字面值

定义函数 :
T operator ""_X( P ) {}

其中T为目标类, _X 为字面值后缀 , P" " 部分的类型 , 作为参数传入(整形 , 浮点 , 字符) .

例子 :

struct Walt { unsigned int v; } 

Walt operator ""_w(unsigned int v)
{
    Walt w ={(unsigned int ) v};
    return w;
}

int main()
{
     Walt w = 1024_w;  // 使用用户自定义字面值
    return 0;
}
auto 自动类型推导

如果你不像写 AAA:::BBB:::CCC::DDD i = GenXXX(); , 试一试 auto i = GenXXX(); .

decltype 提取类型
int i;
decltype(i) j ; // 提取i的类型来定义j

#### 枚举

  • 强枚举

    enum class XXX{… } ;

    • 指定宽弟的枚举 :

    enum class XXX : XXX[ int , char , long … ] { .. } ;

  • 普通枚举

  • enum XXX { … } ;

    常量表达式

    使用 constexpr 修饰的变量或者函数成为编译期间常量, 可以用来声明数组之类 。。 。

    constexpr int i = 10 ; //  修饰变量
    
    /*
    1. 使用之前必须声明
    2. 必须有且仅有一个返回语句
    3. 返回值必须是个常量表达式
    */
    constexpr int GetI() { return 10; } //修饰函数 

    C++98 和 C++ 11 的不兼容

    • >>> >
      • C++98 默认 >> 是右移 运算符 , C++ 11 会考虑是否是模板的嵌套 .
    • auto
      • C++98 没啥用 C++11 是自动类型推导

    慢慢学习~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
相比于 C++98 带来的面向对象的革命性,C++11 带来的 XIII  却并非“翻天覆地”式的改变。很多时候,程序员保持着“C++98 式”的观点来看待 C++11 代码也同样是合理的。因为在编程思想上,C++11 依然遵从了一贯的面向对象的思想,并深 入加强了泛型编程的支持。从我们的观察来看,C++11 更多的是对步入“成熟稳重”的中年 时期的 C++ 的一种改造。比如,像 auto 类型推导这样的特性,展现出的是语言的亲和力 ; 而右值引用、移动语义的特性,则着重于改变一些使用 C++ 程序库时容易发生的性能不佳的 状况。当然,C++11 中也有局部的创,比如 lambda 函数的引入,以及原子类型的设计等, 都体现了语言与时俱进的活力。语言的诸多方面都在 C++11 中再次被锤炼,从而变得更加合 理、更加条理清晰、更加易用。C++11 对 C++ 语言改进的每一点,都呈现出了经过长时间技 术沉淀的编程语言的特色与风采。所以从这个角度上看,学习 C++11 与 C++98 在思想上是 一脉相承的,程序员可以用较小的代价对 C++ 的知识进行更换代。而在现实中,只要修改 少量已有代码(甚至不修改) ,就可以使用 C++11 编译器对旧有代码进行升级编译而获得 标准带来的好处,这也非常具有实用性。因此,从很多方面来看,C++ 程序员都应该乐于升 级换代已有的知识,而学习及使用 C++11 也正是大势所趋。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值