读书笔记

有趣的知识点

  • __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 是自动类型推导

    慢慢学习~~

    你可能感兴趣的:( 读书笔记)