条款二十一

优先使用make_unique(c++14)与nake_shared而非使用new

allccate_shared(分配子, 用以构建管理对象的参数)

1、make系列仅仅会引起一次动态分配;new则是两次,一次给指针,一次给控制块

2、new操作和构造函数之间可能执行其他导致异常的函数

4个例外

1、你需要定制析构器,make系列不可以自定义析构器

2、需要用 “{ }”(初始化列表)进行初始化时,make系列是默认使用()进行参数匹配的。不过也可以分成两步完成:

auto a = {...};             auto s = make_shared(a);

3、被管理类有自定义的new、delete等符号时不要使用make_shared,因为shared_ptr使用全局版本的delete和new时不仅仅释放了管理对象的内存,还要释放控制块的内存。

4、make系列创建的控制块是与管理对象在同一块的,即所有w_ptr与s_ptr都失效才会释放对象,因为那个时候控制块才释放。故如果你的确希望最后一个s_ptr失效时就析构被管理对象,那么你就不应该使用make_shared。

条款二十二

使用Pimpl用法时将特殊成员函数定义放到实现文件中

因为当你使用Pimpl用法时,大概率(其实就是会)要使用unique_ptr来管理分离出的数据类,而unique_ptr的删除器(即默认的delete)决定了其被析构时管理的不能是不完整类型(incomplete type),因为delete会在删除前使用static_assert检测自己删除的是否为完整类型。

对于Pimpl类来说,定义部分被放在了单独的头文件中,这个仅有定义的类中其实仍然包含了一些函数的定义,即特种构造成员的定义,因为系统帮你合成了它们。

这些特种构造函数中,有些是需要进行析构的,如析构函数,移动函数;这也就意味着这些函数需要析构其中的unique_ptr,然而unique_ptr锁管理的类在这个头文件中是只有定义的,即是一个不完整类型(incomplete type),由于析构函数等为隐式inline所以其中析构代码被展开,析构unique_ptr的代码也被展开执行,此时便检测到unique_ptr管理的是一个不完整类型(其实你在实现头文件中定义了这个类,但是在这个声明头文件中无法被看到),使得析构失败,无法通过编译,并抛出类似不完整类型的警告。

因此,要把特种构造成员放到实现文件里去,即使=default也要放过去,这样特种构造成员就可以在实现文件里看到你定义的数据类。

当然,可以看出这里是因为unique_ptr特殊的析构方式,即在自己的析构函数中delete所管理的指针,我们才有必须使用完整类型的限制;若是采用shared_ptr的话便不会出现这个问题,因为shared_ptr的删除器并不是shared_ptr的一部分,而是控制块的一部分,控制块是个完整的类型。

条款二十三

std::move与std::forward

首先我们要明确,所有的参数都是左值,即使这个参数是T&&类型的,那也只是说明其是右值引用类型的左值。只有临时对象是个右值。

std::move强制返回参数对应的右值(要注意移动操作是无法作用于const对象之上的,因为移动操作其实改变了参数)

template<typename T>
decltype(auto) move(T&& t)
{using res = remove_reference_t<T>&&;//using res = remove_reference<T>::type&&;return static_cast<res>(t);
}

相比之下,std::forward<T>(param)则是条件转换。

仅当参数param是右值类型并且T是普通值类型时才会把结果转换成右值。

forward常用在你调用了一个函数后,这个函数内部还需要调用其他函数,而你希望调用的其他函数采用和当前函数一样的版本(如你用右值调用了fun1,fun1调用fun2,若要fun2使用其右值版本,你就可以用forward)。

所以这两个函数其实在运行时不做任何事,只是转换了参数类型。

条款二十四

万能引用与右值引用

在模板函数中担任类型推断的【T&&】(const T&&也不是)才是万能引用,可以接受任何类型的参数;而一般函数的T&&参数只是右值引用,标志此函数接受右值参数。

条款二十五

对右值引用施以std::move而对万能引用施以std::forward

当你要把一个右值/万能引用不止一次地绑定到一个函数内的多次调用的时候,仅仅在最后一次使用move或forward因为要保证之前的调用都不会改变这个对象。(当你move或forward之后很有可能就把参数中的对象搬到调用方函数中去了,原来的对象剩下空壳,此时再一次使用move或forward显然没有意义甚至出错)

当按值返回的函数返回的对象是绑定到右值/万能引用的时候,使用std::move/std::forward来减少复制(如果这个返回的对象是个右值的话就可以直接移动到返回值;如果直接返回,右值引用类型的参数其实是个左值)

但是这一点对于一般的局部对象(值类型T)无效,一方面局部对象是左值,另一方面编译器具有RVO优化或者说NRVO优化,即返回一个等于返回类型的局部变量时编译器不进行拷贝,而是把局部变量“移动”给返回值,相当于编译器自主使用了std::move,所以如果再使用forward或move反而阻止了这项优化。

条款二十六

避免依万能引用进行重载

因为万能引用无论如何都是精确匹配,所以会阻止 需要也可以进行类型转换 的其他重载版本

条款二十七

熟悉依万能引用进行重载的替代方案

首先明确完美转发的好处是避免了函数接受参数时需要创建的临时对象,比如传值的参数若改用传引用就可以省去一次复制,或返回值时创建的临时对象。

1、放弃重载,用多个相似名字来定义不同的需要版本,这个方法自然无法用于构造函数。

2、用const T&代替T&&,达不到完美转发的高效率,但是简洁有效未尝不可。

3、传值,如果知道函数是一定要进行复制的,那就先值传递再使用std::move,这样的话一定程度上效率相当,并且没有重载问题。

4、标签分派,即采用STL泛型算法的分层思想,令用户使用的表层函数采用万能引用,实现功能的实际函数根据一个额外的无名参数来判断需要调用的实际功能函数类型。这一方法对构造函数同样无效,因为系统会合成某些特种成员,被合成的那些成员就无法享受完美转发了(当然你可以手动定义来避免自动合成,但结果没差)。

可能用到的函数:

(1)is_integral<T>()

返回std::true_type/std::false_type来标识参数是否为整型

(2)remove_reference_t<T> /  remove_reerence<T>::type

移除万能引用判断结束后T上的&,得到原本的值类型

5、模板限制

std::enable_if

仅在c++11后,14后具有了_t版本,功能为条件启用模板

使用示例

template< typename T, typename = typename std::enable_if<condition>::type >

.....函数......

模板只会在condition为True时启用,进行判断可能用到的一些函数

std::is_same<T1,T2>::value                      判断是否同类

std::decay<T>::type/std::decay_t<T>        移除T的const/volatile标识符已及引用,即还原到基本类型

std::is_base_of<T1,T2>::value                  判断T1是否为T2的基类

static_assert(condition, "...");                     编译期报错

is_constructible<T1,T2>:value                   判断T2是否可以构成T1

条款二十八

引用折叠

(1)type & && -> type&

当万能引用T&&接受左值时T被推断为type&,故T&&整体的类型为T& &&,折叠后为T&,即左值引用。

(2)type && && -> type &&

当万能引用T&&接受右值时T被推断为type&&,故T&&整体类型为T&& &&,折叠后为T&&,即为右值引用。

条款二十九

假定移动操作不存在、成本高、未使用

移动 操作并非总是优于 复制 操作,比如这些情况:

(1)没有移动操作

(2)移动操作并不比复制快

(3)移动操作不是noexcept的

(4)移动的是个左值对象

实例:

如小字符串优化的string

将对象存储于本体内的array

这两者都将数据存在了对象内,即使使用移动也要把数据逐个移动

移动操作很多时候是因为只要复制指向数据的指针所以才比复制快,所以要把移动操作当做不存在来考虑程序的设计。

条款三十

完美转发失败的情形

一、大括号初始化物

这一点是由于模板没有办法直接判断{ }的类型,若直接将{ }传入模板函数会被判断成其他类型,因为{}可以隐式转换成某些类型,如vector相当于先用{}初始化了vector,再传入模板。但是可以直接指明{ }为initializer_list<T>,即调用模板函数时采取:

fun<initializer_list<T>>( { } );

的形式指明类型。

当然auto可以判断initializer_list的类型,故如下也是可以的:

auto L = {....}

fun(L);

这样模板函数就可以判断L的类型了。

二、0与NULL表示空指针时

0与NULL本质上是某种整型,一般是int,所以模板不可能把它们判断成指针型,这也是为啥应该使用nullptr。

三、仅有声明的整型static const成员变量

如一个声明在类内并拥有初始值的static const int成员,这样的成员由于没有定义在外部,故编译器会使用实际的值来代替变量,这样便不必保存这个变量,即这个所谓的“变量”没有地址,也不必为其保留内存,算是优化。但是问题就出现在这里。万能引用在进行型别判断时由于static const int成员为左值,故T被推导为左值引用即type&,故T&&为type& && -> type&,也就是参数类型是左值引用,所以需要读取传入实参的地址,但是static const int成员又由于编译器的缘故没有地址,所以完美转发无法运作。

四、重载的函数名字和模板名字

因为无法决定使用哪个版本,所以也没法转发

修正方法是先使用相要的函数版本初始化一个指针,或者直接指定具体的模板类型。

五、位域

规定了位域的类成员

因为无法拥有指向比特的指针,故无法转发

指针可以指向的最小单位就是char,又因为指针性质上与引用是同样的。故无法拥有绑定比特的引用,但模板推断出的类型是绑定到位域的指针,而位域又是由机械字的若干部分(即比特)组成的,所以推断会失败。

effective morden c++4相关推荐

  1. effective morden c++ 3

    条款十三 优先使用const_iterator 对于iterator与const_iterator 它们是两个不同的类型,而不是说一个是另一个加上/除去const修饰符,故static_cast.re ...

  2. effective morden c++1

    条款一.模板型别推导 T的推断忽略const(指向目标不可变的const可以推断), volatile等,因为T只是一个副本而已 T& 推断出实际的类型左值/右值而非引用,并且不会把数组型[ ...

  3. effective morden c++ 2

    条款七  区分()与 {} 3中初始化方式 int x(0) int x = 0 int x{0} //相当于int x = {0} int x //没有初始化 在类内成员初始化时等号与大括号均可,但 ...

  4. 《Effective Morden C++》Item 7: Distinguish between () and {} when creating objects

    引子 从本Item开始,我们就进入了第二个章节,本章节,S.M.通过对比C++的新老feature来鼓励大家使用new features. 本Item先从对象初始化开始. 正文 1.基础 C++11中 ...

  5. 《Effective Morden C++》Item 8: Prefer nullptr to 0 and NULL.

    引子 这一条目就比较简单了,就是宣传用nullptr来指代空指针,而不是之前的0或者NULL. 正文 在老式C++中,显然0是int类型,而NULL也是一个整数类型(int或者long).总的来说,这 ...

  6. [2017.02.06] 阅读《Effective Morden C++》

    转载于:https://www.cnblogs.com/ausk/p/6374630.html

  7. Effective C++ 50条款

    Effective C++ 50条款 条款 1:尽量用 const 和 inline 而不用#define--尽量用编译器而不用预处理 #define max(a,b) ((a) > (b) ? ...

  8. [.NET] 《Effective C#》快速笔记 - C# 中的动态编程

    <Effective C#>快速笔记 - C# 中的动态编程 静态类型和动态类型各有所长,静态类型能够让编译器帮你找出更多的错误,因为编译器能够在编译时进行大部分的检查工作.C# 是一种静 ...

  9. Effective STL 50条有效使用STL的经验笔记

    Scott Meyers大师Effective三部曲:Effective C++.More Effective C++.Effective STL,这三本书出版已很多年,后来又出版了Effective ...

最新文章

  1. 百炼智百炼智能获5000万元Pre-A轮融资,深耕智能获客赛道
  2. Zip文件中文乱码问题解决方法(MAC->Windows)
  3. 数据中心冷热空气流控制优化方案
  4. 【CSS3】自定义滚动条样式 -webkit-scrollbar
  5. file is not a zip file_【钢新滨河社团活动】超燃手势舞,牛津A班邀你来battle!
  6. 参加胶东开发者技术大会有感
  7. 让程序间隔执行并可以停止
  8. 如何发布自己的 Composer 包
  9. mysql econnreset_javascript - 节点Js mysql(和mysql2)ECONNRESET - 堆栈内存溢出
  10. SQL 不常用的一些命令sp_OACreate,xp_cmdshell,sp_makewebtask
  11. 微信小程序tabBer图标大小如何自定义
  12. ArcGIS为什么计算面积被禁用
  13. 国土导弹光学反狙击探测系统行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  14. java实现单词默写助手,强烈建议new-hand收藏
  15. 完美解决idea Maven Cannot reconnect
  16. java免费浏览器,Java swing实现简单的浏览器源码免费分享
  17. 解决“Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.”
  18. dell文件服务器连接,配置DELL MD3200存储服务器连接到多台主机
  19. JavaScript网课一
  20. HDU 5804/BC 86A Price List

热门文章

  1. 单片机通信——spi、iic、uart
  2. Bea公司和sun公司介绍
  3. nginx trac mysql svn_TRAC 整合已有的SVN目录
  4. Java中抽象类与方法的重写
  5. LocalDate获取本日所在周的周一和周日
  6. 运放稳定性连载13:RO何时转变为ZO?(2)
  7. Mysql 数据库名 表名 字段名最长长度
  8. 规则引擎在数据治理平台的实践
  9. 2012过年的时间 2012过年放假时间 2012年什么时间过年 2012过年 2012年什么时候过年
  10. 老司机手把手教php,老司机手把手教你玩驱魔!纯小白无脑驱魔攻略