C++学习小结之注意细节点

在学习C++的过程中,有很多需要新手注意的地方,这不仅对找工作的C++程序员有帮助,同时也有利于广大的上班族程序员。

所谓细节决定成败,让我们一起来看看吧!

1、*p++,*(p++),(*p)++
由于后置++是返回当前地址,再对地址自增,所以
*p++:先取当前地址中存放的内容,再将地址自增,同*(p++)
(*p)++:取当前地址中存放的内容,再将该内容自增

2、static
  1. static全局变量只初始化一次,防止其他文件单元引用它
  2. static局部变量只被初始化一次
  3. static函数只在一个源文件内有效

3、c++静态成员
  1. 由类的所有对象共享
  2. 存储在全局数据区
  3. 在类外初始化,在类中声明
  4. 静态成员函数只能访问静态成员

4、ASSERT()是一个宏,存在于debug版本中
      assert()是一个标准库函数,存在于release版本中
注意:它们只捕捉非法情况,而不捕捉错误情况。

5、switch(c)中,c唯独不能是float,可以是int, long, char, unsigned int。

6、不同的字符数组名是不同的,因为它们有各自的存储区,它们的值是存储区的首地址。
例如: char* p1="abc";
            char* p2="abc";
解释:由于“abc”是字符串常量,存储在常量区,所以p1==p2

            char p3[]="abc";
            char p4[]="abc";
解释:数组拥有各自的存储区,所以p3!=p4。

7、malloc/free 和new/delete的区别:
  1. new能自动计算所需内存,malloc需要手动计算所需字节数;例如:int *p1=new int[2],int *p2=(int)malloc(2*sizeof(int))
  2.  new与delete带具体指针类型,malloc与free返回void类型指针
  3. new是类型安全的,malloc不安全
  4. new分类new操作和构造,new操作对应于malloc
  5. new将调用构造函数
  6. malloc/free需要库文件stdlib.h,new/delete不需要

8、引用作为返回值的优点:内存中不产生返回值的副本,提高程序的安全性和效率。
需要注意以下四点:
  1. 不能返回局部变量的引用
  2. 不能返回函数内部new分配的内存的引用
  3. 可以返回类成员的引用,最好是常引用类型
  4. 返回流操作符<<和>>的引用,不能返回+-*/四则运算符的引用
9、栈和堆
  1. 栈是向低地址(向下)扩展的数据结构,是一块连续的存储空间,一般大小是提前分配的,为2MB
  2. 堆是向高地址(  向上)扩展的数据结构,是一块不连续的存储空间(采用链表方式存储),大小一般小于2GB。

10、strlen和sizeof的区别:
strlen:执行的是一个计数器的工作,从内存某处开始扫描,直到碰到第一个字符串结束符'\0'为止,返回计数值。
sizeof:以字节形式给出其操作数的存储大小。

例如:strlen("\0")=0,sizeof("\0")=2

  1. sizeof是关键字,strlen是函数。sizeof后面是类型必须加(),是变量名可不加。例sizeof(int),sizeof a
  2. sizeof可以类型作为参数,strlen只能以char*作为参数,且必须以"\0"结尾,sizeof可以函数作为参数
  3. 当函数名做sizeof参数时,不退化;传递为strlen时退化为指针
  4. sizeof在编译时计算,strlen在运行时计算
  5. 数组作为参数传递给函数时,传递的是指针而不是数组,传递的是数组的首地址

11、字节对齐的3个准则:
  1. 结构体的首地址能被其最宽基本类型成员的大小整除
  2. 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍
  3. 结构体的总大小为结构体最宽基本类型成员大小的整数倍

12、指针与引用的区别:
  1. 指针是存放变量地址的一个变量,逻辑上是独立的,可以改变;引用则是别名,逻辑上不独立,具有依附性,因此必须在一开始就被初始化,且其从始至终只能引用一个变量
  2. 指针传递采用值传递的方式,形参的改变不会影响到实参;引用传递的是实参的地址,对形参的操作会影响到实参
  3. 引用不需要解引用(*),而指针需要
  4. 引用只能在定义时被初始化一次,而指针却是可变的。
  5. 引用不能为空,而指针可以。一个引用对应一个存储单元
  6. 对引用sizeof得到的是所引用的对象的存储大小,而指针的sizeof是指针本身的大小,只与操作系统有关
  7. 指针和引用的自增(++)运算意义不一样
  8. 若返回动态分配的对象或内存时,必须使用指针,引用可能引起内存泄露

13、#define的不足:
  1. 无法进行类型检查。宏定义只是在编译前做字符替换工作
  2. 由于优先级不同,宏定义可能会存在副作用
  3. 无法单步调试
  4. 会导致代码膨胀
  5. c++中,使用宏无法操作类的私有成员

14、含参数的宏与函数的区别:
  1. 函数调用,先求出实参的值再代入形参;带参的宏只是进行简单的字符替换
  2. 函数调用在运行时处理,需要分配临时内存;宏则是在编译时进行,不分配内存,不进行值传递处理
  3. 函数中的实参和形参要定义类型;宏没有类型
  4. 调用函数得到一个返回值,宏可以设法得到几个结果
  5. 宏使源程序变长,函数调用不会
  6. 宏替换不占用运行时间,函数调用占用运行时间(分配单元、保留现场、值传递、返回)
  7. 宏的参数每次要求值,可能导致不可预料的结果,函数调用的参数只在调用前求值一次

15、枚举和define的不同:
  1. 枚举常量是实体中的一种,而宏定义不是实体
  2. 枚举常量属于常量,宏定义不是常量
  3. 枚举常量具有类型,但宏没有类型
  4. #define宏常量在预编译阶段替换,枚举常量在编译时确定其值
  5. 一般编译器,可以调试枚举常量,但不能调试宏常量
  6. 枚举可以一次定义大量相关常量,#define宏一次只能定义一个

16、c++中宏定义与内联函数的区别:
  1. 宏定义在预处理阶段进行代码替换,而内联函数在编译阶段插入代码
  2. 宏定义没有类型检查,内联函数有

17、c++中4种传递参数的方式
  1. 值传递:将实参的值复制到形参,它们不是同一个存储单元,实参不会发生改变
  2. 指针传递:传递的是变量的地址,本质还是"值传递",会改变实参所指的内容
  3. 传引用:实参地址传递到形参,使它们共享同一单元
  4. 全局变量传递

18、重载与覆盖的区别:
重载函数:具有不同的参数列表(参数类型、个数、顺序不同),重载不关系函数的返回值类型
重载的特征:
  1. 相同的范围(在同一个类中)
  2. 函数名相同
  3. 参数不同
  4. virtual关键字可有可无

覆盖:派生类存在重新定义基类的函数,且函数名、参数列表、返回值类型必须与父类中相应的函数严格一致
覆盖的特征:
  1. 不同的范围(分别位于派生类和基类)
  2. 函数名相同
  3. 参数相同
  4. 基类函数必须有virtual关键字

两者的区别:
  1. 覆盖是子类和父类间的关系,是垂直关系;重载是同一类中方法间的关系,是水平关系
  2. 覆盖要求参数列表相同;重载要求不同
  3. 覆盖只能由一个或一对方法产生关系;重载是多个方法间的关系
  4. 重载在编译期间确定调用函数,覆盖在运行期间确定

19、隐藏:派生类函数屏蔽了与其同名的基类函数
屏蔽规则:
  1. 若派生类函数与基类函数同名,但参数不同,则无论有无virtual关键字,基类函数都将被隐藏
  2. 若派生类函数与基类函数同名,且参数相同,但基类函数无virtual关键字,则基类函数将被隐藏

20、面向对象基本特征:封装、继承和多态
21、继承形式:实现继承、可视继承和接口继承

22、使用友元的注意事项:
  1. 必须在内中说明友元函数,可以出现在任何地方
  2. 友元函数不是成员函数,在实现时和普通函数一样
  3. 友元函数不能直接访问类的成员,只能访问对象的成员
  4. 友元函数可以访问对象的私有成员
  5. 调用友元时,在实参中要指出要访问的对象
  6. 类与类之间的友元关系不能继承
  7. 友元声明在类中,实现在类外
  8. 友元函数不能为虚函数

23、复制构造函数与赋值运算符
复制构造函数的特点:
  1. 与类同名,是一种构造函数,没有返回值
  2. 该函数只有一个参数,即某个对象的引用
  3. 每个类必须有一个复制构造函数
  4. 若没有显示定义复制构造函数,则编译器会合成一个默认的
  5. 复制构造函数是新建一个对象实体,所以要保证该对象有独立的内存空间,而不是与其他对象共用
赋值运算:在已经存在的对象上做赋值操作

两者的区别:
  1. 复制构造函数生成新的类对象,赋值运算不能
  2. 复制构造函数不需要检查是否是同一个对象,而赋值运算需要
  3. 若类中有指针类型的成员时,不能使用默认的函数,要显式定义这两个操作

24、位复制:就是把一个对象复制给另外一个对象,他们共享内存空间,那么消除一个对象的指针,会使得另外一个对象的指针成为野指针,一般默认的复制构造函数和默认的赋值运算就是采取位复制。

25、 基类的构造函数和析构函数不能被继承

26、
  1. const对象和引用类型只能初始化,不能被赋值
  2. 类的成员的初始化顺序只与变量在类中的声明顺序有关,与构造函数的初始化列表顺序无关
  3. 静态成员先于实例变量,父类成员先于子类成员,父类构造函数先于子类构造函数

27、变量的初始化顺序
  1. 基类的静态变量或全局变量
  2. 派生类的静态变量或全局变量
  3. 基类的成员变量
  4. 派生类的成员变量

28、3种继承方式
  1. 公有继承:基类对象对基类成员的访问性保持不变;派生类可以访问基类的public和protected成员,不能访问基类的私有成员
  2. 保护继承:基类对象对基类成员的访问保持不变;派生类中,基类的public和protected都作为派生类的protected成员,可以被访问,不能访问基类的私有成员
  3. 私有继承:基类对象对基类成员的访问保持不变;基类的所有成员在派生类中都是私有的,不能被访问

29、使用默认参数的注意事项:
  1. 若函数有多个默认参数,则形参分布中,默认参数应从右至左逐渐定义
  2. 默认参数调用时,调用顺序从左至右逐个调用
  3. 默认值可以是全局变量、全局常量或者一个函数
  4. 默认参数的函数调用是在编译时确定的,所以默认值不能为局部变量

30、只能使用初始化列表而不能用赋值的情况:
  1. 类中含有const或引用的成员变量
  2. 基类的构造函数都需要初始化列表
  3. 成员类型是没有默认构造函数的类

31、虚函数的注意事项:
虚函数的作用:在运行阶段动态的选择合适的成员函数
  1. 只需在函数声明时加上关键字virtual,定义时不需要
  2. 基类中某一成员函数为虚函数,则派生类中的同名函数自动为虚函数
  3. 若声明某个成员函数为虚函数,则该类中不同有与该函数同名,且返回值、参数个数、类型都相同的非虚函数,派生类中也不行
  4. 非成员函数不能为虚函数,全局函数、静态成员函数和构造函数不能为虚函数;析构函数可以为虚函数
  5. 普通派生类对象,先调用基类构造,再调用派生类构造
  6. 基类指针指向派生类时,若基类析构函数不是虚函数,则delete时只调用基类析构函数;若基类析构函数为虚函数,则先调用派生类析构再调用基类析构
  7. 基类指针动态建立派生类对象,普通调用派生类构造函数
  8. 指针声明不用调用构造函数

32、c++中多态的种类:参数多态、引用多态、过载多态、强制多态

33、声明虚函数的注意事项:
  1. 只有类的成员函数才能声明为虚函数
  2. 静态成员函数不能为虚函数
  3. 内联、构造函数不能为虚函数
  4. 析构函数可以为虚函数,且通常声明为虚函数

















你可能感兴趣的:(C++,C++学习总结)