C++基础要点摘记
1.定义是创建与符号关联的实体。声明是让符号为程序所知。
声明变量的同时也就完成了变量的定义,只有声明外部全局变量的情况是例外,当一个文件想要使用其他文件定义的全局变量,则必须声明。
函数和变量的声明不会分配内存, 但是定义会分配相应的内存空间,变量名就是对相应的内存单元的命名
函数和变量的声明可以有很多次, 但是定义最多只能有一次
2.将static全局变量写在头文件中,所有头文件的操作函数都会共享这个变量。但如果是在源文件中去操作这个静态全局变量,则这个静态全局变量只能在当前文件有效,但是在另外一个文件访问此静态变量,会是该变量初始的默认值,不会是其他文件中修改的值,虽然它们有相同的初始内容,但是存储的物理地址并不一样。
一般用法:static修饰全局变量或函数,并未改变其存储位置及生命周期,而是改变了其作用域,好处如下:(1)不会被其他文件所访问(2)其他文件中可以使用相同名字的变量,不会发生冲突(3)全局函数变成静态函数。
static修饰局部变量,一般用在函数中,生命周期从程序开始到结束,作用域是函数的编译单元,每次调用函数操作的是同一个静态变量。
静态成员变量不可以在类内初始化,因为这样会为每个对象创一个,不可以在头文件的类外初始化,因为这样会为每个include了它的源文件创一个(参考此点开头的说明)。在类外初始化时不加static。但是可以用const修饰static数据成员在类内初始化,因为常数据成员。
静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员。因为静态成员函数参数不含this指针。
const成员变量可以在类声明中初始化(类似普通成员变量),或者只能在构造函数的初始化列表初始化,因为const定义的时候必须初始化,不能进行赋值。
static const成员变量可以在类声明中初始化,也可以用类中的无名的private enum替换它定义int的作用。
3. 重载、覆写(重写override)、隐藏、(从名称空间上作答)实例化=生成对象、抽象类一般用作声明函数接口供继承
4. struct和union都是由多个不同的数据类型成员组成,union中只存放了一个被选中的成员,所有成员共用一段内存空间,即对于union的某成员赋值,将会覆盖其它成员,而struct的所有成员都占有自己的内存空间。一个struct变量的总长度等于所有成员长度之和,Union变量的长度等于最长的成员的长度。
类、Struct的对齐:首先根据结构体内部成员的对齐值和系统指定对齐值得到结构体的自身对齐值,取较小的,以此对齐值进行数据放置,与内存补齐,连续的数据可以和并再补齐。(如果一个数据大小不足对齐值的整数倍则放入内存中的时候对它进行补齐,使它占有对齐值整数倍的空间,使得每个数据的开头地址都是补齐值的整数倍)
1.数据类型自身的对齐值 2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack(n),n=1,2,4,8,16改变系统的对齐值
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
5. 多态。动态多态(运行期多态):程序在运行时才决定调用哪个函数(函数地址在运行时而不是编译时根据引用或指针指向的对象与函数符号绑定即动态绑定,动态联编),一个接口,多种方法,实现了父子类的接口复用。封装通过struct\class和三个访问控制权来完成,为了保护内部数据成员,隔离外部干扰,也使代码模块化方便复用。继承可以扩展已存在的代码,为了代码复用。静态多态(编译期多态)是通过重载和模板技术实现。
动态多态由虚函数和继承,虚函数主要通过虚函数表(V-Table)实现。如果一个类包含虚函数,那么就会拥有一张虚函数表(在编译器就建立),虚函数表存储的每一项是一个虚函数的入口地址。对象不包含虚函数表,只有隐藏成员--虚指针(在运行期生成,指向虚表,方便在运行期根据对象查询到底执行哪个函数,即动态绑定。虚指针存在于对象实例地址的最前面,保证虚函数表有最高的性能),派生类会生成一个兼容基类的虚函数表。



继承的虚函数和自己创建的虚函数,可以看到虚表放在基类对象虚表的位置上。
具体看https://www.cnblogs.com/LUO77/p/5771237.html
www.cnblogs.com/fanzhidongyzby/archive/2013/01/14/2859064更详对象模型,有adjustor。
这些图都是编译器可以查看的类内存布局
多(重)继承:派生类继承多个基类,为每个基类(显式或隐式地默认私有)指定了访问级别。派生类的对象包含每个基类的对象,派生类构造函数的初始化列表(因为要先构造出基类对象)调用基类构造函数(否则将调用基类的默认构造函数,然后可以在函数体内对基类对象部分进行初始化,或者不初始化),基类构造函数按照基类构造函数在类派生声明(而不是初始化列表)中的出现次序调用,析构总是按构造函数运行的逆序调用析构函数。
而基类的析构函数最好写成virtual,否则用基类指针对子类对象delete的时候,无法销毁子类对象资源造成资源泄露(effective C++的建议是在类有至少一个virtual函数时,给类声明一个virtual析构,还说即使没有其他virtual,如果会被继承,还是要virtual析构)。有时候想让一个类成为抽象类但苦于没有哪个函数好作为纯虚函数,可以让析构成为纯虚,而且要定义它,否则链接器会抱怨,既解决了子类对象不销毁问题,又使类成为抽象类。
虚基类(虚继承)让子类对象中只拥有一个共享的基类对象副本(如在菱形继承中,子类的父类都继承了同一个基类(公共基类),导致子类对象中可能出现多个基类对象),解决二义性,即解决了公共基类的多份拷贝的问题。
子类对象中包括各基类对象副本,其中每个基类(MyClassA和MyClassB)和公共基类(MyClass)对象有自己的虚函数表指针,每个虚拟继承了MyClass的父类对象还需要记录一个指向虚基类表vbtable的指针vbptr

注:最下面两层是虚基类,虚基类表第一项记录着当前子对象相对于虚基类表指针的偏移,第二项是虚基类对象
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
