1. 让自己习惯C++(Accustoming yourselfto C++)
条款01: 视C++ 为一个语言联邦(ViewC++ as a federation of languages)
C++已经不是单一的语言,而是一个由相关语言组成的联邦。它由若干次语言组成,分别为:
(1) C。区块(blocks) 、语句(statements) 、预处理器(preprocessor) 、内置数据类型(built-in datatypes) 、数组(arrays) 、指针(pointers) 等统统来自C。
(2)Object-OrientedC++。这部分是C with Classes 的: classes (包括构造函数和析构函数) ,封装(encapsulation) 、继承(inheritance) 、多态(polymorphism) 、virtual 函数(动态绑定) ……
(3)Template C++。这是C++ 的泛型编程(genericprogramming) 部分,也是大多数程序员经验最少的部分。就是所有的东西都是模板,与具体的类型没有关系
(4)STL。 STL 是个template 程序库,包括容器(containers) 、迭代器(iterators) 、算法(algorithms) 以及函数对象(functionobjects)。
所以,要想让C++高效,首先我们必须弄清楚我们现在是处在上面4种的哪一个语言层次,每一个语言层次我们关注点是不同,高效编程的规则也不一样,例如,对于内置类型:pass by value比较好,对于用户自定义,用pass by reference to const比较好,对于STL,还是得用pass by value 。所以,想编牛逼的程序,先弄清楚自己在用C++的什么层次
条款02: 尽量以const,enum, inline替换#define(Prefer consts,enums,and inlines to #define)
本条款讨论了C语言中的#define在C++程序设计中的带来的问题并给出了替代方案。
C语言中的宏定义#define只是进行简单的替换,对于程序调试,效率来说,会带来麻烦。在C++中,对于单纯变量,最好用const或enums对象代替#define;对于形似函数的宏(macros),最好用inline代替#define。
编译过程:.c文件--预处理-->.i文件--编译-->.o文件--链接-->bin文件
预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。检查包含预处理指令的语句和宏定义,并对源代码进行相应的转换。预处理过程还会删除程序中的注释和多余的空白字符。可见预处理过程先于编译器对源代码进行处理。预处理指令是以#号开头的代码行。
#defineASPECT_RATIO 1.653 这个不好,为什么呢?记号名称ASPECT_RATIO不能被编译器看见,不方便debug,盲目宏替换导致目标代码出现多份,没有类型检查,替代方法:const floatASPECT_RATIO = 1.653
#define不能搞出类的专属常量,但是const+static可以
classGamePlayer{
private :
static const int NumTurns = 5 ; //声名式,并非定义式
}
如果非要一个定义式,则在实现文件,非头文件中写:const int GamePlayer::NumTurns = 5
如果,类的某个成员还依赖NumTurns , 那么,就用class里的enum
classGamePlayer{
private :
enum( NumTurns = 5) ;
int score[NumTurns] ;
}
常量替换#define两点注意:
· 定义常量指针:
const char*authorName = “Shenzi”;
cosnt std::string authorName("Shenzi");
· 类专属常量:
static const int NumTurns= 5;//static 静态常量 所有的对象只有一份拷贝。
万一你编译器不允许“static整数型class常量”完成“in
calss初值设定”(即在类的声明中设定静态整形的初值),我们可以通过枚举类型予以补偿:
enum { NumTurns = 5 };
*取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址通常也不合法。如果你不想让别人获取一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束。
宏可以搞小函数,但是麻烦的YB,记得加各种括号,还要想到各种安全性的东西,替代方法就是用inline
当然,预处理器还是有用的,#include, #ifndef 之类还是很有用的,预处理器包括宏定义,文件包含,条件编译
关注ifndef和externword,学习了extern关键字(加C或者加变量两种情况)。
条款03: 尽可能使用const(Useconst whenever possible)
本条款总结了const的使用场景和使用它带来的好处。
const就是加上一个约束,不能改变
1、关键字在星号左边,表示被指物事常量 const char *p和charconst *p两种写法意义一样,都说明所致对象为常量;
2、关键字在星号右边,表示指针自身是常量
3、constvector<int>::iterator iter 表示iter自身是常量,不能瞎指
4、vector<int>::const_iteratoriter 表示iter所指对象是常量,不能通过iter改变
上面只是一般的东西,const最狠的地方是函数的声明(参数,返回值,函数自身)
参数和返回值都比较好理解,关键是函数自身
1、关键点:两个成员函数如果只是常量性不同,可以实现重载。改善C++要用pass byreference to const,如果有相应的const函数多好。
2、const函数,只能操作类的const成员,但是。如果给变量加上mutable关键字,那么,const成员函数也能对private成员变量赋值修改。
3、应当运用const成员函数实现出其non-const的孪生兄弟版本,不能反过来。即用non-const版本调用const版本可避免代码重复。移除const的动作const_cast,加上const的动作static_cast,见条款27。
条款04: 确定对象被使用前已先被初始化(Makesure that objects are initialized before they’re used)
本条款告诫程序员,在C++程序设计中,在使用对象之前,应该对其初始化,以避免不必要的错误。同时,给出了高效初始化对象的方法和正确初始化对象的方法。内置对象,显示初始化,类对象,通过构造函数初始化。
(1)构造函数最好使用成员初值列(member initialization list) ,而不要在构造函数本体内使用赋值操作(assignment) 。类对象的初始化,在进入类的构造函数之前就已经完成了,所以,如果在构造函数中用赋值,相当于1次default ctor + 1此copy assignment , 如果直接用初始化列表,那么就是1次copy ctor 或者 1此default ctor ,显然后者效率更高。初值列列出的成员变量,其排列次序应该和它们在class 中的声明次序相同。
构造函数的运行顺序是先祖先,后儿子,析构相反,自己中按照类中变量的声明顺序初始化,而不是初始化列表中的顺序
(2)C++对“定义于不同编译单元内的non-local static对象”的初始化次序无明确定义,所以最好以local static对象替换non-local static对象,具体如下所示。这也是设计模式中Singleton模式的一种常见实现手法。
class
FileSystem {
... };
FileSystem& tfs()
//用这个函数来代替non-local static对象
{
static FileSystem fs;
// 以localstatic的方式定义和初始化对象
return fs; //
返回一个引用,指向local static对象
}
本文深入探讨了C++编程中高效编程的关键准则,包括如何将C++视为一个语言联邦,合理使用const、enum和inline替代#define,以及如何在使用const、初始化对象和选择关键词时提高代码质量。此外,文章还强调了使用const的重要性,提供了关于构造函数、静态成员函数和初始化顺序的详细指导,旨在帮助开发者编写更高效、更易于维护的C++代码。
&spm=1001.2101.3001.5002&articleId=14452413&d=1&t=3&u=c699f0d3eab143f199151242fd8f6b4d)
1017

被折叠的 条评论
为什么被折叠?



