Item 2 理解auto类型推导
条款2:理解 auto 类型推导auto 类型推导和模版类型推导有一个直接的映射关系,他们之间可以通过⼀个⾮常规范⾮常系统化的转换流程来转换彼此。
在Item1中,模版类型推导使用下面这个函数模版来解释:
12template<typename T>void f(ParmaType param); //使⽤⼀些表达式调⽤f
在 f 的调⽤中,编译器使⽤ expr 推导 T 和 ParamType 。当⼀个变量使⽤ auto 进⾏声明时, auto 扮演了模板的⻆⾊,变量的类型说明符扮演了 ParamType 的⻆⾊。
12345auto x = 27;const auto cx = x;const auto & rx=cx;
在这⾥例⼦中要推导 x rx cx 的类型,编译器的⾏为看起来就像是认为这⾥每个声明都有⼀个模板,然后使⽤合适的初始化表达式进⾏处理:
123456789101112template<typename T> //理想化的模板⽤来推导x的类型void func_for_x(T param);func_for_x(27);templ ...
Item 3 理解decltype
条款3:理解decltypedecltype 可以得出一个名字或者一个表达式的类型,通常是一个精确的结果,但有时候也会出错。
简单的例子:
12345678910111213141516171819const int i=0; //decltype(i)是const intbool f(const Widget& w); //decltype(w)是const Widget& //decltype(f)是bool(constWidget&)struct Point{ int x; //decltype(Point::x)是int int y; //decltype(Point::y)是int};template<typename T>class Vector{ ... T& operator[](std::size_t index); ...}vector<int> v; //decltype(v)是vect ...
Item 4 会查看类型推导结果
IDE编辑器在IDE中的代码编辑器通常可以显⽰程序代码中变量,函数,参数的类型,你只需要简单的把⿏标移到它们的上⾯,比如:
1234const int theAnswer = 42;auto x = theAnswer;auto y = &theAnswer;
一个IDE编辑器可以直接显示 x 的推导结果为 int ,y 的推导结果为 const int* ,为此,代码应该尽可能处于可编译状态,因为IDE之所以能提供这些信息是因为⼀个C++编译器(或者⾄少是前端中的⼀个部分)运⾏于IDE中。如果这个编译器对你的代码不能做出有意义的分析或者推导,它就不会显⽰推导的结果。
编译器诊断另⼀个获得推导结果的⽅法是使⽤编译器出错时提供的错误消息。这些错误消息⽆形的提到了造成我们编译错误的类型是什么。
比如:
12template<typename T> //只对TD进⾏声明class TD; //TD == "Type Displayer"
如果这时候尝试实例化这个类模板,就会出现一个错误消息,因为这里没有用来实例化的类模板定义。为了查看 x 和 y ...
Item 6 auto推导若⾮⼰愿,使⽤显式类型初始化惯⽤法
条款6假设我有一个函数,参数为 Widget,返回一个 std::vector<bool> ,这⾥的 bool 表⽰ Widget 是否提供⼀个独有的特性。
1std::vector<bool> features(const Widget& w);
更进⼀步假设表⽰是否 Widget 具有⾼优先级,我们可以写这样的代码:
123bool highPriority = features(w)[5];...processWidget(w,highPriority);
但是如果我们使⽤auto代替显式指定类型做⼀些看起来很⽆害的改变:
123auto highPriority = features(w)[5];...processWidget(w,highPriority); //未定义⾏为!
就像注释说的,这个 processWidget 是⼀个未定义⾏为。为什么呢?答案有可能让你很惊讶,使⽤ auto 后 highPriority 不再是 bool 类型。虽然从概念上来说 std::vector<bool> 意味着存放bool,但是 st ...
Item 5 优先考虑auto而⾮显式类型声明
条款5:优先考虑 auto 而非显示类型声明对一个局部变量使用解引用迭代器的方式初始化:
12345678template<typename It>void dwim(It b, It e){ while(b!=e){ typename std::iterator_traits<It>::value_type currValue = *b; }}
声明⼀个局部变量,变量的类型只有编译后知道,这⾥必须使⽤ typename 指定,因为待决名的原因。
auto 变量从初始化表达式中推导出类型,所以我们必须初始化。
12345678template<typename It>void dwim(It b,It e){ while(b!=e){ auto currValue = *b; ... }}
因为 auto 使用了 Item2 所描述的类型推导技术,它甚至可以表示一些只有编译器才知道的类型: ...
Item 7 区别使⽤ () 和 {} 创建对象
条款7:区别使用 () 和 {} 创建对象C++ 11 有三种初始化对象的语法选择,一般来说初始化值要用 () 或者 {} 括起来或者放到 = 的右边
12345int x(0); //使⽤小括号初始化int y = 0; //使⽤"="初始化int z{0}; //使⽤花括号初始化
在很多情况下,可以使用 = 和 {} 的组合
1int z = {0};
在接下里的笔记里,忽略 = 和 {} 的组合初始化语法,因为 C++ 通常把它视作和只有 {} 一样。
混乱地使用 = 初始化可能会有一些误导,让别人以为这里是赋值运算符。对于像 int 这样的内置类型,研究两者区别是没有多⼤意义的,但是对于 ⽤⼾定义的类型 而⾔,区别赋值运算符和初始化就⾮常重要了,因为这可能包含不同的函数调⽤:
12345Widget w1; //调⽤默认构造函数Widget w2 = w1; //不是赋值运算符,调⽤拷⻉构造函数w1 = w2; //是⼀个赋值运算符,调⽤operator=函数
甚至在 C+ ...
C++29种未定义行为
开启标准库的调试模式
未定义行为?
空指针类
1. 不能解引用空指针(通常会产生崩溃,但也可能被优化产生奇怪的现象)
2. 不能解引用 end 迭代器
3. this 指针不能为空
指针别名类
4. reinterpret_cast 后以不兼容的类型访问
5. union 访问不是激活的成员
6. T 类型指针必须对齐到 alignof(T)
算数类
7. 有符号整数的加减乘除模不能溢出
9. 左移或右移的位数,不得超过整数类型上限,不得为负
10. 除数不能为 0
函数类
11. 返回类型不为 void 的函数,必须有 return 语句
12. 函数指针被调用时,不能为空
生命周期类
13. 不能读取未初始化的变量
14. 指针的加减法不能超越数组边界
15. 可以有指向数组尾部的指针(类似 end 迭代器),但不能解引用
16. 不能访问未初始化的指针
17. 不能访问已释放的内存
18. new / new[] / malloc 和 delete / delete[] / free 必须匹配
19. 不要访问已经析构的对 ...
多态基础第一部分
一、多态基础
虚函数在函数前面加上 virtual 就是声明虚函数,virtual 说明符指定非静态成员函数为虚函数并支持动态调用派发。
它只能在非静态成员函数的首个声明(即当它在类定义中声明时)的 声明说明符序列 中出现。(声明说明符序列:在C语言中,声明说明符序列(declaration specifiers)是用来声明变量、函数或类型的一部分,它们定义了类型和属性。一个典型的声明说明符序列包括存储类说明符、类型说明符、类型限定符和函数说明符。)
虚函数调用在使用有限定名字查找(即函数名出现在作用域解析运算符 :: 的右侧)时被抑制。
虚函数的继承
虚函数的继承体现了接口继承
继承了接口等于继承了函数的壳,这个壳有返回值类型,函数名,参数列表,还包括了缺省参数
只需要重写/覆盖接口的实现(函数体)
虚类/虚基类含有虚函数的类叫做虚类
是虚类且是基类的叫虚基类
重写/覆盖
条件:三同:函数名,参数,返回值都要相同
概念:重写/覆盖是指该函数是虚函数且函数的名字、类型、返回值完全一样的情况下,子类的函数体会替换掉继承下来的父类虚函数 ...
多态基础第二部分
二多态原理
引入(多态原理)计算下面虚类的大小:
12345678910111213class Base{public: virtual void func() {}private: int _a; char _b;};int main(int argc, char* argv[]){ std::cout<<sizeof(Base)<<"\n"; return 0;}
如果是一般的类,那我们会认为是计算结构体对齐之后的大小,结果应当是 8。
但计算结果发现,虚类的结果是 12 ,说明虚类比普通类多了一些东西.
实例化对象Base b; 可以发现对象的头部多了一个指针 _vfptr; 这个指针叫做虚函数表指针,它指向了虚函数表
虚函数表指针指向虚表的指针,叫虚函数表指针,位于对象的头部.
定义:
如果在类中定义了虚函数,则对象中会增加一个隐藏的指针,叫虚函数表指针__vfptr,虚函数表指针在成员的前面,直接占了 4/8 字节.
虚函数 ...