C++学习--知识点归纳

来源:互联网 发布:刘烨的演技 知乎 编辑:程序博客网 时间:2024/05/01 06:09
1. static的作用
静态变量:
全局变量:只初始化一次,防止在其他文件单元中被引用; 局部变量:只初始化一次,下一次依据上一次结果值;
静态全局变量:在模块内(但在函数体外),它的作用域范围是有限制的,它可以被模块内的所有函数访问,但不能被模块外的其它函数访问。
静态函数:
只是在声明他的文件当中可见,不能被其他文件所用
类静态数据成员:
存储在全局数据区,定义时分配空间,所以不能再类声明中定义,初始化是在类外,初始化时不再需要带static。
不属于特定的对象,为本类的所有对象共享。
类静态成员函数:
不是与任何对象相联系,不具有this指针;
可以直接引用本类中的静态数据成员,而不访问非静态成员
它无法访问属于类对象的非静态成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。


2. volatile 类型修饰符
用来修饰被不同线程访问和修改的变量。被volatile类型定义的变量,系统每次用到它的时候都会直接从对应的内存中提取,而不会利用到cache中的值


3. a++与++a
a++不能当做左值,而++a可以当做左值


4.new/delete VS. malloc/free
new能自动计算需要分配的内存空间,而malloc需要手动计算;
new与delete直接带具体类型的指针,malloc与free返回void类型的指针;
new的类型安全性更高;
new操作可以重载,可以自定义内存分配策略;
new将调用构造函数,而malloc不能;delete将调用析构函数,而free不能
malloc/free需要库文件stdlib.h,new/delete不需要库文件支持;


5. 引用
作为返回值时:
不能返回局部变量的引用;
不能返回函数内部new分配的内存的引用;
可以返回类成员的引用,但最好是常引用类型;
支持流操作符<<和>>


6. strlen 和sizeof
sizeof以字节形式给出了操作数的存储大小,strlen是从内存的某个位置开始扫描,直到碰到第一个字符串结束符'\0',然后返回计数器值;
sizeof可以用类型甚至是函数作为参数,而strlen只能用char*做参数,而且必须是以"\0"结尾;
大部分便一起的sizeof都是在变异的时候计算的,因此可以通过sizeof(x)来定义数组的维数,而strlen的计算则是在运行起计算的,用来计算字符串的实际长度,不是类型占内存的大小。


7. 字节对齐
一般满足以下3个准则:
a. 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
b. 结构体每个成员相对于结构体首地址的偏移量都是成员的大小的整数倍,如有需要编译器会在成员之间加上填充字符;
c. 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要便一起会在最末一个成员之后加上填充字节。


8.概念比较
函数指针与指针函数


9. C++参数传递方式
值传递;指针;引用;全局变量传递


10. 重载、覆盖与隐藏
重载指函数不同的参数表,对同名函数的名称做修饰,不关心函数的返回值(不同的返回值类型不构成重载)
成员函数重载:1.相同的范围(同一类中);2.函数名相同;3.参数不同;4.virtual关键字可有可无;
覆盖:派生类中重新定义基类的函数,其函数名、参数列、返回值必须同父类中的相对应函数严格一致,只有函数体不同
覆盖的特征:1.不同的范围(基类和派生类)2.函数名相同;3.参数相同;4.基类函数必须有virtual关键字
隐藏:派生类的函数屏蔽了与其同名的基类函数
1. 如果派生类的函数与基类函数同名,但参数不同,则无论有没有virtual,基类的函数都将被隐藏;
2. 如果派生类的函数与基类函数同名,且参数相同,但基类函数没有virtual关键字,此时基类函数被隐藏


11.C++中函数调用方式
函数堆栈:(1)在进入函数之前,保存“返回地址”和环境变量;(2)在进入函数之后,保存实参或实参复制、局部变量;
调用约定:实参或实参复制入栈、出栈、函数堆栈释放的方式,在win32下,有以下4种:
_cdecl
_stdcall
_thiscall
_fastcall


12. 数组
一维数组:数组名代表数组中第一个元素的位置,即地址;
二维数组:数组名可理解为指向数组的指针,例a[3][4],a+1相当于&a[1]


13. 局部变量和全局变量
局部变量可以与全局变量重名,但是局部变量会屏蔽全局变量。要使用全局变量,必须使用操作符::。
(1)全局变量的作用于为这个函数块,而局部变量的作用域为当前函数;
(2)内存存储方式不同,全局变量在全局数据区,局部的分配在栈区;
(3)生命周期不同;
(4)使用方式不同;通过声明后局部变量可以再程序的各个部分用到


14. 交换两个变量的值
(1)普通+-法:  a=a+b; b=a-b; a=a-b;
(2)异或法: a=a^b; b=a^b; a=a^b;


引申:找出两个int的最大和最小值(不用判断语句)
(1)max=((a+b)+abs(a-b))/2; min=((a+b)-abs(a-b))/2;
(2)对变量的差值移位(右移31位),通过其是否为0判定变量的正负性;
(3)直接移位判定。int i=(unsigned(a-b)>>31)+(unsigned(b-a)>>31)*2; 如果>,则i=2,如果=,i=0,如果<,i=1;
(4)max=a+(((b-a)>>31)&(b-a));  min=a-(((a-b)>>31)&(a-b));


15. strcpy & memcpy
strcpy和memcpy都是从一块内存复制一段连续的数据到另一块内存,区别是终结标志不同。strcpy逐个字节复制内容直到'\0',二memcpy是复制确定的n个字节


16. 面向对象和面向过程
(1)出发点不同
(2)层次逻辑关系不同
(3)数据处理方式与控制程序方式不同
(4)分析设计与编码转换方式不同


17. 深拷贝和浅拷贝
如果一个类拥有资源(堆或者是其他系统资源),当这个类的对象发生复制过程时,资源重新分配,这个过程就是深拷贝;反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。


18. 拷贝构造函数和赋值运算符
拷贝构造函数:
(1)函数名与类名相同,因为也是一种构造函数,没有返回类型
(2)只有一个参数,为某个对象的引用;
(3)没各类都必须有一个拷贝构造函数,如果没有显示定义,那么编译器会自动生成一个缺省的拷贝构造函数;
(4)目的是建立一个新的对象实体,所以一定要保证新创建的对象有独立的内存空间,而不是与先前的对象共用。
(5)不允许拷贝构造函数传值参数,否则编译出错。如果传值,就会在拷贝构造函数内调用拷贝构造函数,就会形成无休止的递归调用导致栈溢出。


赋值操作符(=):用已经存在的对象来创建另一个对象,给对象赋予一个新的值
定义赋值运算符函数时,注意几点:
(1)是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身的引用?因为只有返回引用,才可以允许连续赋值。
(2)是否把传入的参数的类型声明为常量引用?如果传入的参数不是不是引用而是实例,那么从形参到实参会调用一次拷贝构造函数。
(3)是否释放实例自身已有的内存?如果忘记在分配新内存之前释放已有空间,程序会出现内存泄露。
(4)是否判断传入的参数和当前的实例(*this)是不是同一个实例?如果同一个,则不进行赋值,直接返回。否则,会在释放实例自身的内存时出现严重的问题。


区别:
(1)拷贝构造函数生成新的类对象,而赋值运算符不能;
(2)拷贝构造函数是直接构造新对象,所以在初始化这个对象前不用检查源对象是否和新建对象相同,而赋值运算符则需要;
(3)当类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值操作符复制构造函数,不能使用默认的。


19. 初始化列表与构造函数初始化
二者堆内置类型的成员没有什么大的区别,在性能和结果上都是一样的。对于非内置类型的成员变量(类),在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体后,进行的是对已经构造好的类对象赋值,又调用一个赋值操作符才能完成。
在很多情况下,需要使用带初始化列表的构造函数。
(1)类中有const或引用成员变量时,二者只能初始化,不能对它们赋值。
(2)积累的构造函数都需要初始化列表;
(3)成员类型是没有默认构造函数的类。若没有提供显示初始化,编译器会隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器的尝试将会失败。


20. 空类默认产生的成员函数
6个:默认构造函数、复制构造函数、析构函数、赋值运算符重载函数、取址运算符重载函数、const取址运算符重载函数


21. 默认参数的函数

默认参数在函数声明中提供,当既有声明又有定义时,定义中不允许给出默认参数值。
默认参数有多个是,在形参表中默认参数从右至左逐渐定义。调用时,从左到右逐个调用。
如果一组重载函数都允许相同实参个数的调用,将会引起调用的二义性。


22. 多态性
虚函数实现多态。虚函数的本质就是通过基类访问派生类的函数。每个含有虚函数的类,起实例对象内部都有一个虚函数表的指针。该虚函数表指针初始化为本类的虚函数表的内存地址。所以在程序中,不管对象类型如何转换,但该对象内部的虚函数表指针是固定的,这样才能实现动态地对对象函数进行调用。这就是C++多态性的原理。
多态性的种类:参数多态、引用多态、过载多态、强制多态等。


23. 虚函数
不能声明为虚函数:
(1)只有类的成员函数才能声明为虚函数;
(2)静态成员函数不能为虚函数;
(3)内联函数不能为虚函数;
(4)构造函数不能为虚函数;
(5)析构函数可以,且通常应声明为虚函数。


24. 虚继承、虚基类、纯虚函数和抽象类
虚基类
:将共同基类设置为虚函数,解决二义性,防止双份拷贝间接基类。虚基类的声明是在派生类的定义中进行的,以虚继承的方式;
虚基类的构造函数:(1)虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用。如果未被列出,则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象。(2)从虚基类直接或间接继承的派生类中的构造函数的成员初始化列表中都要列出这个虚基类构造函数的调用。但是,只有用于建立对象的那个最派生类的构造函数调用虚基类的构造函数,而该派生类的基类中所列出的对这个虚基类的构造函数调用在执行中被忽略,这样便保证了对虚基类的对象只初始化一次。(3)在一个成员初始化列表中出现对虚基类和非虚基类构造函数的调用,则虚基类的构造函数先于非虚基类的构造函数的执行。

(参考:http://blog.csdn.net/prochsh/article/details/2915252)


虚继承:class 派生类名:virtual 继承方式 基类名
在gcc中,不管是否虚继承,虚表指针在整个继承关系中是共享的,而不共享的是指向虚基类的指针。

在VC编译器中,根据是否为虚继承来判断是否在继承关系中共享虚表指针(虚继承时虚表指针不共享,而普通继承时父类和子类是共享虚表指针的),而对指向虚基类的指针,和gcc一样是不共享的。


纯虚函数:在许多情况下,在基类中不能对虚函数给出有意义有实现,而把它说明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。定义方式为:virtual   <类型><函数名>(<参数表>)=0;  

抽象类:带有纯虚函数的类称为抽象类。抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,它处于继承层次结构的较上层。抽象类只能作为基类来使用,是不能定义对象的,在实际中为了强调一个类是抽象类,可将该类的构造函数说明为保护的访问控制权限。在一个继承层次结构中,由它来为它们提供一个公共的根,相关的子类是从这个根派生出来的。刻画了一组子类的操作接口的通用语义,这些语义也传给子类。一般而言,抽象类只描述这组子类共同的操作接口,而完整的实现留给子类。


25. const

(1)定义常量
(2)指针中使用
        (a)指针本身是常量不可变
             (char*) const pContent;
             const (char*) pContent;
       (b)指针所指向的内容是常量不可变
             const (char) *pContent;
             (char) const *pContent; 
        (c)两者都不可变
              const char* const pContent;
(3)函数中使用
(4)类成员变量
        表示成员常量不能被修改,同时它只能在初始化列表中赋值。
(5)类成员函数
        a. const成员函数不被允许修改它所在对象的任何一个数据成员。
        b. const成员函数能够访问对象的const成员,而其他成员函数不可以。

0 0