effective modern C++读书笔记

来源:互联网 发布:ftp服务器默认端口号 编辑:程序博客网 时间:2024/04/30 12:32

类型推导:

1.ParamType是reference或pointer,不是universal reference

有引用则忽略引用,其余部分匹配T

template <typenmae T>void f(T& param);int x = 27;const int cx  =x;oonst int& rx = x;f(x);//T is int, paramtype is int&f(cx);//T is const int, paramtype is coint int&f(rx);//T is const int, paramtype is coint int&
2.paramType是universal reference

1)如果是lvalue,T被推导为引用,paramType也是引用

2)如果是rvalue,使用1的规则

template<typename T>void f(T&& param);int x = 27;const int cx = x;const int & rx = x;f(x);//x is lvalue, T is int&, paramtype is int&f(cx);//x is lvalue, T is const int&, paramtype is const int&f(rx);//x is lvalue, T is const int&, paramtype is const int&f(27);//27 is rvalue, T is int, paramType is int&&
3.非引用,非指针

expr中有reference,忽略掉;有const,忽略掉;有volatile,忽略掉。

template<typename T><pre name="code" class="cpp">void f(T param);int x = 27;const int cx = x;const int & rx = x;
//以下全部T paramtype都为 intf(x);
f(cx);
f(rx);

auto的推导规则与template基本相同,除了以下:

auto于初始化braced initializer时初始化为std::initializer_list,而template type deduction不会

auto x = {1,2,3}; //类型是std::initializer_list<int>

tips:

auto作为函数返回或lambar参数时时template type deduction的方式。

此外,使用auto的情况,尤其是初始化对象的时候必须是类型明确的时候。




modern C++:

7.区分创建对象时的(),{}

e.g:

std::vector a(3,0);  //创建3个0的vectorstd::vector b{1,2,3};//创建1,2,3的vector


8-15:使用新标准的关键字

nullpter代替0,

使用using代替typedef,

使用delete删除不必要的函数,

使用override表明函数覆盖,

使用const_iteratorter替代iterator,

使用noexcept在函数无异常时,

使用constexpr对编译器就能编译的函数


16.使函数具有线程安全性

C++11具有新的内存模型,支持多线程编程


17.理解编译器生成的函数

6种函数:default constructor,destructor,copy operations(copy constructor,copy assigment),move operations

move operation只在类缺少move operation,copy operation and destructor时生成

copy constructor只在缺少明确的copy constructor时生成,在move operation声明时删除。

copy assignment opetaor只在只在缺少明确的copy assignment opetaor时生成,在move operation声明时删除。

函数模板不能阻止特殊函数生成


智能指针:

独占资源使用std::unique_ptr,共享资源使用std::shared_ptr,配合std::shared_ptr使用std::weak_ptr;

使用std::make_unique,std::make_shared代替创建对象(不违反成对出现new,delete的原则),另外传给这两个函数的是作为被创建对象的参数。

小心循环引用。

auto_ptr复制的时候是浅复制,指向底部的区域是同一块。


右值语义,移动语义,perfect forwarding:

std::move将对象转换为rvalue;std::forward转换为rvalue仅当参数绑定为右值,其他情况都为左值;std::move,std::forward都是编译器转换

universal reference指对象满足类型推导和&&时;universal reference转为rvalue只有参数为rvalue时,否则全为lvalue;

std::move配合rvalue使用,std::forward配合universal reference使用;

universal reference使用时避免重载

e.g:

templte<typename T>void logandadd(T&& name){...};void logandadd(int idx){...};logandadd(22);   //cals int overloadshort nameidx;logandadd(nameidx);  //error

以及解决这种情况的方案:在pass by lvalue-reference-to-constant, pass by value, tag dispatch, 放弃重载之间抉择

reference collapsing:编译器生成a reference ti a reference,结果变为一个单独的reference;只有两个都是rvalue reference才会得到rvalue reference,否则都是lvalue reference

编译器生成后产生的情况:

template<typename T>void func(T&& param);Widget widget();Widget w;func(w);//call func with lvalue;func(widget());//call func with rvalue
熟悉std::forward失败场景,如braced initializers,0或NULL或null pointers,重载函数和模板函数,bitfield

lambda表达式:

避免默认捕获,针对每个捕获参数设置&或者=;

&捕获时,外部被捕获的对象值在生命期必须大于该闭包。否则,会出现闭包调用时,该对象已经析构;


使用init capture移动参数进入闭包

auto func = [pw = std::make_unique<Widget>()]{...};

配合auto&&和std::forward使用(C++14)

C++多线程:

选择task-based比thread-based好。任务通常独立,降低了线程与线程间的关联,减少了线程同步的使用,自然更容易使用

std::launch::async关键字使用但异步必要的时候,launch的介绍

std::thread在所有路径上都unjoinable,对线程要么join,要么detach

使用std::future替代条件变量,(高级同步原语替代基本同步原语)

使用std::atomic,volatile修饰多线程共享变量(后者让编译器不优化,atomic的介绍

杂项:

类添加对象进入私有容器时,对lvalue和rvalue参数的处理:

1.使用重载函数,一个使用lvalue参数,一个使用rvalue参数

2.使用universal reference

3.传值

三种方案的消耗:

1.一次复制对lvalue,一次移动对rvalue

2.如上

3.在函数体重,值参数被move进容器,因此比上面两种多一次move,无论lvalue还是rvalue


使用emplacement系列函数代替插入

1.减少了途中的操作,更高效

vector的emplace

vectoe的emplace_back



0 0