书摘
生活必须被适当的搁置和隐蔽,暴露太多的人会显示脆弱,因其丝毫不知道后退和隐藏,留给彼此余地。有时候,人与人之间只隔了一道墙;有时候,只需要隔一扇门;有时候,只需隔一层花、一株柳的隐约相望,可是,偏偏不能有一丝的接近。
语言联邦
C++最初是在C的基础上夹杂一些面向对象的特性,而这个语言逐渐成熟以后,她变得更加活跃和自由。如今的C++已然支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式,这些能力和弹性使C++成为一个无可匹敌的工具,所以当我们看待C++时,要将其看作一个语言联邦而不是单一的某种语言。
命名
用技术来创造艺术,优雅而又精准的变量、函数、类的命名可以大幅度提升你的程序的可读性。
这里介绍一种: 匈牙利标记法
虽然这个命名方法有许多令人诟病的地方,但是我认为它的优点大于缺点。当然,还有其他的命名方法,可以找一种适合自己的哦。
此处举个小栗子:
/*变量名: 变量的类型首字符加上变量的意义,每个不同单元的首字符大写*/
int iAge = 30; // i代表int,Age代表变量的意义是年龄,A大写代表分界。
int iNumber = 4; //个数
float fAverageAge = iAge / iNumber;
上面的代码看出问题了吗?没错,两个整型变量相除的结果是7而不是7.5,得到的结果是int型而不是float型。如果没有类型的标记,相信大多数人都会认为这只是一个普通的除法运算吧,标记的好处也体现了出来。 但是当我们要更改fAverageAge为int型时,我们不只是需要修改float为int 还要修改fAverageAge为iAverageAge,这也是它被诟病的地方之一。
及时初始化
在变量的定义时,我们最好直接为其初始化,以免编译器随意赋值带来的弊端。保证对象的初始化在类中的构造函数的参数表中用缺省值完成。
举个例子:
class A
{
public:
int display()
{
return a;
}
A(int a = 0): a(a){}
private:
int a;
};
A m;
int b = A.display(); //b的值是0,证明成功初始化了A::a。
你可能会想,int a;和int a = 0;能有多大的区别呢?确实不大,但是你是否忽略了指针的存在呢?
先看代码:
int iA = 5;
int* ipPoint;
ipPoint = &4;
上面的代码有没有出错的可能性呢?
因为指针被赋予随机值,那么有没有这样一种可能:
iA存储在最后的四个字节中,那么当ipPointer的位置被随机分到位置1时不会出现问题,因为存储空间足够,如果ipPointer被分配到了位置2呢?会不会破坏iA的数据?会的,此时程序出现了问题,但是编译器不会报错,为了避免这种问题,变量在定义时最好初始化。
减少宏定义出现的频率
尽可能用enum、const、inline替换宏定义。但是要切记,现在的C++并不能做到完全剔除宏定义,该用的时候还是要用。
inline主要是替换宏函数。
#define PI 3.14
宏定义在编译期间会被全部替换掉,也就是说,在编译期间所有的PI都会被替换成3.14,那么我们使用了多少次PI就会占用多少个3.14的存储空间,而且,如果 int a = PI,在某些编译器上的警告会是:隐式类型转换3.14,而不会提示隐士类型转换 PI,如果这个PI使我们自己定义的还好,我们可以找到它,如果是别人在别的文件中定义的呢?你还能找到这个3.14的来源吗?所以,为了避免这种麻烦,采用常量来代替宏定义,如下:
const float PI = 3.14;
这时,无论你调用多少次PI,都只会占用一个3.14的内存,而且出现问题也会提示PI而不是3.14,优势尽显。
类中的常量
先来看一段代码:
class A
{
private:
//声明静态常量a。
static const int a;
};
//定义式
const int A::a = 3;
这个是在类中封装一个静态常量a,注意写法,在类外面定义,在类内的是声明!!
无论如何,这是#define做不到的。
类的构造、析构、赋值运算
赋值
首先我们要了解一下,在类中,编译器为我们创造了哪些默认函数。
默认构造函数、析构函数、默认赋值运算符。
也就是说,只要是同一个类的对象,可以互相赋值。
class A
{
public:
int display()
{
return a;
}
A(int a = 0): a(a){}
private:
int a;
};
A a(5);
//下面的表达是正确的。
A b = a;
但是有时候我们不希望出现赋值运算,那么我们就应该明确的拒绝它,难不成要修改编译器?不不不!
我们只需要把赋值运算符的重载函数作为一个私有成员就行了。
class A
{
public:
int display()
{
return a;
}
A(int a = 0): a(a){}
private:
//声明为私有成员。
A& operater + (A& b);
int a;
};
A a(5);
//下面的表达是错误的。
A b = a;
这样就可以避免赋值的出现了。
析构函数
如果该类作为基类出现程序,那么最好把析构函数声明为虚函数, 否则在派生类的对象析构时,会出现不知调用哪个析构函数的问题。
class A
{
public:
A();
//析构函数 虚函数
virtual ~A();
private:
//还记得这个吗,复习一下。
static const int n;
};
class B : public A
{
/* 省略内容 */
};
//应该还记得吧。
const int n = 5;
构造函数
不要在构造函数和析构函数中调用虚函数!!
总结
大部分篇幅是为了解释清楚为什么这么做,而需要怎么做已经在高亮字体中了。