第一章:关于对象
1:C++每个类对象的内存大小由:类中非静态成员内存的和,指向虚函数表的指针和内存对齐的内存三者共同决定。
内存如何对齐,我以前写过一篇博文里讲的很详细。
2:虚函数表里存放的是虚函数的内存地址,对象调用虚函数就是通过虚函数表找到它的地址从而调用它,这也是多态的实现原理。
3:struct和class除了默认的权限不同外,其它基本一样;在只使用数据成员时用struct
第二章:构造函数语音学
C++的编译器,在你没有定义构造函数的情况下,会自动生成一个默认构造函数,但这个构造函数可能是trivial(无用的)即相当于没有生成;也可能是nontrivial(有用的),有用的默认构造函数才是真正的被编译器合成出来,有用的构造函数也不是会显示设定所有成员的默认值。
有以下四种情况合成的默认构成是有用的,也就是真正合成了的
- 类中含有一个类对象,而且这个类对象含有默认构成函数,因为这样编译会自动合成默认构造函数来调用这个类对象的构成函数,就是有用的了
- 含有虚函数,合成的默认构造函数会产生虚函数表来存放所有虚函数的地址和指向这个虚函数表的指针
- 继承自一个有默认构造函数的基类,合成默认构造函数里要调用基类的默认构造函数去初始化基类部分
- 继承自一个虚基类,要初始化虚基类表和指向虚基类表的指针,虚基类表里放的是虚基类里成员的偏移。
编译器还会自动合成copy constructor,编译自动合成的拷贝构造函数是位逐次拷贝的,若类中含有指针或引用时,位逐次拷贝可能不安全。自动合成没有位逐次拷贝的情况也是上面说的那四处情况。
程序转化,编译器会把我们写的程序转化它自己的认识的方式来运行。比如参数初始化,返回值初始化等,其中重要的是NRV优化,即不用拷贝构造函数,就是把返回值放入形参的引用中。
若不想使用系统合成的,可以声明一个拷贝构造函数但不定义,把它设为私有。
第三章:Data语音学
这章主要讲的是类对象的内存布局
1.每一个类对象的内存由非静态成员,内存对齐和虚指针组成。整个对象的存放位置是呢是根据它在程序中的位置而定,其中的静态成员存放在静态存储区,类似于静态数据成员不属于对象而属于类了,非虚函数呢类似全局函数,不过它有一个隐式的第一个参数this指针,指向调用它的对象。
2.在继承的过程中,非静态的数据都会继承到子类中,复杂的是虚函数表的变化和指向虚函数的指针。
- 在单一的普通继承中派生类的虚函数表会继承基类的虚函数表并且会添加子类虚函数的地址,若重写了,则子类虚函数的地址会覆盖掉虚函数表中被重写的函数地址。这样在调用虚函数通过虚函数表时会产生多态。在这种情况下继承后派生类只有一个虚函数表
- 在多继承中,虚函数会有多个虚函数表,派生类自己的虚函数的地址会添加在继承序列的最左边那个类的虚函数表中
- 在虚继承中,若派生类的虚函数完全重写了则只有一个虚函数表,否则会有多个。
第四章:函数语意学
主要说的就是虚函数
在继承过程中子类的虚函数表会有三种变化:
1.从基类中拷贝一份虚函数表
2:在虚函数表中添加一个自己的slot
3:在虚函数表中占有基类的slot,即重写。
第五章:构造、析构、拷贝语意学
1:构造,初始化类的对象,在单一继承中很好理解,就是先初始化基类,再初始化派生类,依次推下去,但在虚拟继承中,会导致重复的构造虚基类,解决方法:在每个派生类有条件的构造虚基类,若条件为false则不构造虚基类。
2:析构的顺序跟构造相反,一层一层的析构,析构完派生类,剩下基类,继续析构基类
3:拷贝过程中也会遇到虚基类多次重复拷贝,没有什么太好的解决方法,只有尽量抑制虚基类的拷贝或虚基类中不要有成员
第六章:执行期间语意学
1:在对象刚开始声明时编译内部会调用它的构造函数,在结束时会调用它的析构函数
2:全局对象和局部对象它们的析构时期不一样
3:new是从堆区开辟一个内存空间,并返回那个空间的指针,要和delete配套使用,使用的过程中,注意异常安全。
4:临时性对象的产生和销毁
第七章:站在对象模型的尖端
1:模板,当类或函数中有模板时,编译会很少的发现错误,因为它的类型检查等其它检查在模板实例化时才开始
2:模板类的成员函数,只有在用到时才会被实例化
3:模板中名称决议法:当会模板实例化无关时,用模板声明区域的名称;当与模板实例化有关时,用模板实例区域的名字
4:异常处理的三步,try throw catch