高质量C++编程指南
- 修饰符的位置,应将*和&紧靠变量名
int *x,y;//此处y不会被认为是指针变量,right int* x,y;//此处y会被认为是指针变量,wrong
- 在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。
- 如何建立在整个类中都恒定的类成员变量?
不要指望const,比如有一个类成员变量const int a,因为不能在声明中初始化a,只能在构造函数中初始化它,而对于这个类的不同对象,可以初始化不同a值。要想使a在所有类对象中都恒定,可以使用枚举,enum { SIZE1 = 100, SIZE2 = 200};枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数。
- 内存管理
内存分配方式有三种:
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
指针与数组的对比:
C++/C程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。(注意:数组名是对应一块内存,而不是指向,两者有明显区别)
在修改内容时:
在内容复制与比较时:char a[]="hello";char b[10];b=a;不能通过编译,然而char *p = (char *)malloc(sizeof(char)*(strlen(a)+1));p=a;虽然在逻辑上是错误的,但是能通过编译。这是因为数组名是对应一块内存,而不是指向,当然不能简单通过=号赋值。也可以通过sizeof(a)=6,sizeof(char *)=4来证明此结论
有了malloc/free为什么还要new/delete
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。 因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
- C++的高级特性
对比于C语言的函数,C++增加了重载(overloaded)、内联(inline)、const和virtual四种新机制。其中重载和内联机制既可用于全局函数也可用于类的成员函数,const与virtual机制仅用于类的成员函数。
隐形类型转换导致二义性
void output(int x);void output(float x);//声明 output(0.5)//编译错误,由于数字本身没有类型,将数字当作参数时将自动进行类型转换,编译器不知道该将0.5转换成int还是float类型的参数。(像1和2之类的整数例外,编译器会把它们转为int型,如output(1)不会发生错误)
成员函数的重载、覆盖与隐藏
重载:在同一个类中,函数名相同,参数不同
覆盖:分别属于派生类和基类中,函数名和参数都相同,基类函数必须有virtual(也就是通常说的派生功能)
隐藏:它也是处于派生类和基类中(重点)
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
内联
C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码,“断言assert”恐怕是唯一的例外
- 类的构造函数,析构函数,赋值函数
基类的构造函数、析构函数、赋值函数都不能被派生类继承。如果类之间存在继承关系,在编写上述基本函数时应注意以下事项:
1.派生类的构造函数应在其初始化表里调用基类的构造函数。
2.基类与派生类的析构函数应该为虚(即加virtual关键字)。