《effective c++》学习笔记(一)
2017-08-23 19:25
281 查看
尽量以const, enum, inline替换#define
使用const,enum定义常量
当使用define时,实际上define出的名字并没有进入符号表。从而导致许多莫名其妙的错误,例如:#define INF 123 int &b = INF;
那么编译器则会提示
invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
这是因为引用b需要一个左值,而INF却是一个右值。
所以当我们定义常量时,可以使用
const或
enum来定义,而当需要限制常量的范围,可以使用
static const
使用inline定义较短的函数
观察这一份代码#define MAXNUM(a, b) (a > b ? a : b) int a = 1; int b = 5; int c = MAXNUM(++a, b); std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl;
输出符合我们的预期,a、b、c分别是2、5、5,但是如果a大于b的话
#define MAXNUM(a, b) (a > b ? a : b) int a = 5; int b = 1; int c = MAXNUM(++a, b); std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl;
那么此时a、b、c的值分别是7、1、7,而我们预期应该是6、1、6。
因为define只是进行简单的替换,所以当a大于b时,则会执行两次++a,这并不是我们设计这个define的初衷。
所以此时我们可以使用inline函数来代替define。
inline int MAXNUM(int a, int b) { return a > b ? a : b; } int a = 5; int b = 1; int c = MAXNUM(++a, b); std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl;
此时无论a和b谁大,执行结果都是符合预期的,并且inline也会展开函数,并不会带来函数压栈退栈的额外开销。
对于单纯常量,最好用const对象或enum替换#define
对于形似函数的宏,最好改用inline函数替换#define
尽可能使用const
根据“最小权限”原则:最小特权原则是系统安全中最基本的原则之一。所谓最
小特权(Least Privilege),指的是”在完成某种操作时所赋予网络中每个主体(用户或进程)必不可少的特权”。最小特权原则,则是指”应限定网络中每个主体所必须的最小特权,确保可能的事故、错误、网络部件的篡改等原因造成的损失最小”。
所以当一个对象能以read权限完成工作的话,就不要赋予他write权限,以保证错误(比如编译错误)发生率最低,看以下这个例子:
class Foo { // something }; Foo operator*(const Foo &lhs, const Foo &rhs) { // something } int main() { Foo a; Foo b; Foo c; if (a * b = c) { // something } return 0; }
当我们手误写出
a * b = c时,实际上想表达的是
a * b == c,但此时编译器并不会报错,因为调用了Foo::operator=。但如果我们将operator*的返回值声明为const类型,则编译器会告诉我们“不能对一个const对象赋值”,那么就可以避免这种错误。
class Foo { // something }; const Foo operator*(const Foo &lhs, const Foo &rhs) { // something } int main() { Foo a; Foo b; Foo c; if (a * b = c) { // something } return 0; }
将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体
编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量属性”
当const和non-const成员函数有实质等价的实现时,令non-const版本调用const版本可避免代码重复
确定对象被使用前已先被初始化
由于c++的对象在有些时候会初始化,有些时候不会初始化,所以我们尽量对所有变量都进行初始化,以保证行为符合我们的预期。类成员初始化
并且要注意的是,在class的构造函数中,成员初始化放在初始化列表,而不是放在函数体内,考虑下面两份代码:class Foo { Foo(int x, const string& y) { a = x; b = y; } private: int a; string b; }; ///////////////////////////////// class Foo { Foo(int x, const string& y) : a(x), b(y) { } private: int a; string b; };
虽然最后产生的结果相同,但是第一份代码实际上会先调用a和b的默认构造函数,接着再调用一次赋值操作。而第二份代码则仅仅调用一次a和b的构造函数。
那么当赋值操作消耗性能比较大的时,第一份代码会很耗时间。
不同编译单元间初始化
将每一个cpp文件看作一个编译单元的话,考虑下面三个文件://a.cpp #include <iostream> int a = 123456789; void print_a() { std::cout << "a = " << a << std::endl; } //b.cpp #include <iostream> extern int a; int b = a; void print_b() { std::cout << "b = " << b << std::endl; } //main.cpp #include <iostream> void print_a(); void print_b(); int main() { print_a(); print_b(); return 0; }
b的初值依赖于a的初值,那么如果b在a之前初始化的话,则a和b的值不会相等,而c++中不同编译单元的初始化顺序是不确定的,所以不能保证a和b的初始化顺序谁前谁后。
解决办法是,可以在b.cpp中定义一个函数,将b定义为local static对象,因为C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。
//a.cpp #include <iostream> int get_a() { static int a = 123456789; return a; } void print_a() { std::cout << "a = " << get_a() << std::endl; } //b.cpp #include <iostream> int get_a(); int b = get_a(); void print_b() { std::cout << "b = " << b << std::endl; } //main.cpp #include <iostream> void print_a(); void print_b(); int main() { print_a(); print_b(); return 0; }
为内置型对象进行手工初始化,因为c++不保证初始化它们
构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同
为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象
相关文章推荐
- 《effective c++》学习笔记(一)
- 《effective c++》学习笔记(四)
- 《effective c++》学习笔记(五)
- 《effective c++》学习笔记(七)
- 《Effective C++》学习笔记条款13 以对象管理资源
- 《Effective C++》学习笔记条款23 宁以non-member、non-friend替换member函数
- 《Effective C++》学习笔记条款30 透彻了解inlining的里里外外
- 《Effective C++》学习笔记(二)
- 《Effective C++》学习笔记(三)
- 《Effective C++》学习笔记——条款21
- 《effective c++》学习笔记(一)
- 《effective c++》学习笔记(四)
- 《effective c++》学习笔记(五)
- 《effective c++》学习笔记(七)
- 《Effective C++》学习笔记条款04 确定对象被使用前被初始化
- 《Effective C++》学习笔记条款06 若不想使用编译器自动生成的函数,就该明确拒绝
- 《Effective C++》学习笔记条款24 若所有参数皆需类型转换,请为此采用non-member函数
- 《Effective C++》学习笔记条款31:将文件间的编译依存关系降至最低
- 《Effective C++》学习笔记——条款27
- 《effective c++》学习笔记(一)