1.视C++为多语言联合
C++ 最初的名字是 C with Classes,表示一开始,C++ 只是 C 加上一些面向对象特性。
现在的 C++ 已经是一个多重泛型编程语言(multiparadigm programming language)。同时支持:
1)过程形式(procedural)
2)面向对象形式(object-oriented)
3)函数形式(function)
4)泛型形式(generic)
5)元编程形式(metaprogramming)
C++可以分为四个主要部分:
1)C。C++是以C为基础的。区块(blocks)、语句(statements)、预处理器(preprocessor)、内置数据类型(built-in data types)、数组(arrays)、指针(pointers)等统统来源于C。
2) Object-Oriented(面向对象的)。这部分即为 C with Classes,包含:classes(构造及析构函数)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定)等等。
3)Template(模板)。这是C++的泛型形式(generic)。它威力强大,带来了崭新的编程泛型,也就是所谓的template metaprogramming ( TMP,模板元编程)。
4)STL(Standard Template Library,标准模板库)。对容器(containers)、迭代器(iterators)、算法(algorithms)、函数对象(function objects)进行了整合。
在使用不同的部分时,可能需要遵循不同的编程规范。
2.尽量让编译器替换预处理器
在编写程序时,尽量用 const, enum, inline 替换 #define
1)const
如下代码:
#define rate 1.63
在代码预处理时,所有的 rate 将被替换为 1.63。编译过程中,变量名称 rate 并没有进入记号表(symbol table)。故当你遇见使用此常量导致的编译错误信息时,错误提示可能包含 1.63 ,而不是 rate。如果 rate 被定义在一个非你所写的头文件内时,你将很难定位到这个错误。最好替换为如下代码:
const double rate = 1.63
此时 rate 作为一个常量,在编译阶段会进入记号表。此外在某些时候,使用常量可能比 #define更加节省空间,因为预处理器“盲目”的将所有 rate 替换为1.63,可能导致目标码( object code)出现多个1.63,而改用常量并不会出现此种情况。
有两个特殊情况需说明:
1)定义常量指针与指针常量。
a = 10;
b = 20;
const int *p1 = &a; //特点:指针的指向可以修改,指针指向的值不可以修改
// *p1 = 30; 指针指向的值不可以修改
p1 = &b; //指针的指向可以修改
int *const p2 = &a; //特点:指针的指向不可以修改,指针指向的值可以修改
*p3 = 30; //指针指向的值可以修改
// p3 = &b; //指针的指向不可以修改
2)class专属常量。为了将常量的作用域限制与 class 内,必须将其称为 class 的一个成员,为了保证此常量至多只有一份,需将其声明为 static 成员。
staic 静态成员:静态成员变量、静态成员函数
静态成员变量:
1.所有对象共享同一份数据
2.在编译阶段分配内存
3.类内声明,类外初始化
静态成员函数:
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
class person{
public:
//类内声明
static int age;
int a;
//静态成员函数
static void func(){
age = 150;
//a = 100;// 静态成员函数只能访问静态成员变量,无法区分是哪个对象的成员
cout << "静态成员函数" << age << endl;
}
};
//类外初始化
int person::age = 100;
顺带一提, #define 不能提供任何封装性,其生效区间在 #define 到 #undef 之间,针对过程生效。
2)enum
万一你需要在类内进行初值设定时(例如定义数组大小),可以使用使用 enum hack,其理论基础是“一个属于枚举类型(enumerated type)的竖直可权充 ints 被使用”。故代码可定义如下:
class Group{
publice:
enum {num = 10};
int person[num];
};
(enum 用法可见C++-枚举类型(enum)_彭同学她同桌的博客-CSDN博客_c++ enum)
3)inline
还有一个常见的 #define 误用情况是用它实现宏(macros)。
宏看起来像函数,但不会招致函数调用(function call)带来的额外开销。
#define func(a,b) ((a) > (b) ? (a) : (b))
int main() {
for (int i = 0 ; i < 10 ; i++) {
cout << i << "<=" << func(i, i + 1) << endl;
}
// 0<=1 1<=2 2<=3 3<=4 4<=5 5<=6 6<=7 7<=8 8<=9 9<=10
return 0;
}
在上面的例子中,在每个 for 循环的内部调用 func(a, b) 的地方都换成了((a) > (b) ? (a) : (b)),这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。
但是使用 #define 定义宏会带来很多麻烦。无论何时使用宏时,必须为宏中的所有实参加上小括号,否则某些时候会遇到麻烦。但即使你加上了小括号,也会出现如下麻烦:
#define func(a,b) ((a) > (b) ? (a) : (b))
int main() {
int a = 5;
int b = 0;
cout << func(++a,b) << " a:" << a << endl;
// 7 a:7 a被累加2次
a = 5;
b = 0;
cout << func(++a,b + 10) << " a:" << a << endl;
// 10 a:6 a被累加1次
return 0;
}
在这里,调用 func(a, b)之后,a 的自增次数竟然取决于它和谁比较。当 a = 5 与 b = 0 对比时,在 (a) > (b) 时,a 自增一次,输出 (a) 时,a 再自增一次;当 a = 5 与 b + 10 = 10 对比时,在 (a) > (b) 时,a 自增一次,输出 (b) 时,a 不再自增。
此时,你可以使用 template inline 函数获得宏带来的效率以及一般函数的所有可预料行为和类型安全性(type safety)。
template <class T>
inline const T func(T a,T b) {
return a > b ? a : b;
}
int main() {
int a = 5;
int b = 0;
cout << func(++a,b) << " a:" << a << endl;
// 6 a:6
a = 5;
b = 0;
cout << func(++a,b + 10) << " a:" << a << endl;
// 10 a:6
return 0;
}
此时,不需要在函数本体中为参数加上括号,也不用担心参数被计算多次等等问题。
另外,func()是一个真正的函数,其遵守作用域和访问规则,此时你可以将其作为 class 内的 private 函数,一般而言,宏无法完成此事。
此外,template 可以生成一整群函数,每个函数都可以接受两个同类型对象。
(inline:C++ 中的 inline 用法 | 菜鸟教程)
(template:C++模板——template_原来45的博客-CSDN博客_c++ template)
有了 const、enum、inline,我们对预处理器(特别是#define)的需求降低了,但是并非完全消除,其仍是必须的,且 #ifdef / #ifndef 也继续扮演控制编译的重要角色。
(C++中 #ifdef 和#endif的用法与作用详解_inCorning的博客-CSDN博客_endif)
3.总结
1. C++ 已经是一个多重泛型编程语言, 在使用C++不同的部分时,可能需要遵循不同的编程规范。
2. 对于单纯常量,最好以 const 对象或者 enum 替换 #define。
3. 对于形似函数的宏(macros),最好改用 inline 函数替换 #define。