条款七
区分()与 {}
3中初始化方式
int x(0)
int x = 0
int x{0} //相当于int x = {0}
int x //没有初始化
在类内成员初始化时等号与大括号均可,但是小括号不行
对于不可复制的对象则不可用"="
对于大括号而言{}不允许进行隐式的窄化内建型转换(高精度 -> 低精度,因为低精度存储不了高精度){int, double}若要double转换成int,那就通不过编译
{} 另一种特性,可以消除调用默认构造函数确被判断成函数声明的情况
int i(); //函数声明
int i{} //默认初始化
{}特殊处:
使用{}初始化会优先采用以initializer_list<T>的构造函数,且若能使用initializer_list<T>就不会把其他类型的构造函数加入到候选名单中{ int, double}将优先选择initializer_list<T>为参数的构造函数比如 construct(initializer_list<int> li)但是由于double需要窄化转换,故无法使用这个构造函数,即使同时也有一个construct(int, double)版本的构造函数,这个构造函数也不会加入候选名单。
故除非无法匹配initializer_list<T>中的任何一个,比如使用{string, string}来调用构造函数,string无法转化成int故不会选择initializer_list<T>版本。
最后一个特例出现在空的{}时,此时会默认选用一般的默认构造函数,如果要使用空的initializer_list<T>调用initializer_list<T>版本构造则需要{{ }}两个大括号或({})一个小括号里有一个大括号
条款八
优先使用nullptr而非0或是NULL
0或是NULL本质的类型为int和某种整型,故进行函数的调用时0与NULL会会被推断成实际的整型,nullptr则是指针型
所以也正是因此,要避免整型和指针类型的重载(因为有人会用0或者NULL)
条款九
优先使用using声明
using声明允许模板化
template<typename T>
using type = vector<T>;
type<int> ==> vector<int>
等价的typedef则需要借助一个struct
template<typename T>
struct s{
typedef type vector<T>;
};//而且使用起来名字也长了不少
条款十
优先选择使用限定了作用域的enum
enum class et1{....} -----> 限定了作用域
enum et2{..} ------> 未限定作用域的
未限定作用域的et2内部的变量名字和et2处在同一个作用域,而et1则是像一个类一样包含其内部变量的。限定作用域还阻止了枚举型到相应的数值类型的隐式自动转换,需要借助static_cast才能将这些名字转换成相应数值类型。另一点则是enum class可以进行前置声明如:
enum calss et1;
而未限定作用域则需要指定类型后才可,如:
enum et2 : int;
有一种特殊情况下不限定作用域会更加方便,即使用tuple时:
tuple<t1,t2,t3...> tu;
auto t = get<i>(tu);//取出tu中的第i个成员引用即ti类型的引用
此时使用不限定作用域的enum可以直接代替i来使用,而限定作用域的enum class则需要static_cast转换成size_t类型再操作。
当然,可以写一个返回constexpr类型的函数来解决这个问题。
template<typename T>
typename auto toUtype(T enumerator)
{
return static_cast<typename std::underlying_type_t<T>>(enumerator);
}//其实是返回enum class成员的原本数值类型
条款十一
优先使用delete而非private未定义函数
delete函数在写下代码时就可以检测出错误,private未定义函数则需要等到链接才行。
另外delete可以用于一般函数(用来作用于重载函数可以排除不接受的参数类型)甚至可以作用于模板函数的特化版本
(要注意模板特化不可以出现在类内)
条款十二
为改写函数加上override
可以防止你在派生类中的同名函数有任何声明形式上的不同
如:
参数
const修饰
引用修饰
返回值
异常规格
基类对应成员函数为virtual
另外这两个关键字可以做作为函数名字使用,因为override和final是只有在声明末尾的时候才会被解释成关键字
如 void fun() final -> 关键字
void final() -> 名为final的函数
此外
由左引用&修饰的函数如:void fun() &,应当返回左值;而右引用&&则应当返回右值。
因为右值引用修饰的函数只会被右值类实例调用,故调用函数的类实例的内部成员应当会在调用结束后被释放(因为这个类实例会被释放),所以不能返回左值(因为他们会被释放,你不能返回一个被释放成员的引用)。