习惯C++
# 习惯C++
# Item 01:视C++为一个语言联邦, View C++as a federation of languages.
C++是一个语言联邦, 有多种编程范式: procedural (面向过程), OOP(面向对象), GP(泛型编程), Functional Programming(函数式编程),metaprograming(元编程),但差不多有四个主要的部分:
- C:数组, 指针, 预处理, 程序员的自我修养
- Object oriented C++: classs, encapsulation, ingeritance, polymorphism, virtual function的动态绑定;(模板, 异常, 重载)
- Template C++ (template metaprogramming, 模板元编程)
- STL: 容器(containers)、迭代器(iterators)、算法(algorithms)以及函数对象(function objects)
# Item 02:尽量以const,enum,inline替换#define, Prefer consts,enums,and inlines to#defines.
用const, enum, inline代替 #define
const 能够更好地控制指针和指针所指变量的const属性, 并且#define对作用域不敏感,const可以定义某个类专属的常量;
const int GamePlayer::NumTurns;
1class专属常量。为确保此常量至多只有一份实体,你必须让它成为一个static成员:
class GamePlayer{ private: static const int NumTurns = 5; // 常量声明 int scores[NumTurns]; // 使用该常量 }
1
2
3
4
5enum是不可以取地址的,enum hack是模板原编程的基础技术,见Item 48
#define带来的安全性可以使用模板inline函数实现, 见Item 30
template<typename T> // 暂时用模板的typename代替 inline void callWithMax(const T& a, const T& b){ f(a > b ? a : b); }
1
2
3
4对于单纯常量,最好以const对象或enums替换#defines。
对于形似函数的宏(macros),最好改用inline函数替换#defines。
# Item 03:尽可能使用const, Use const whenever possible.
该用const的地方,尽可能使用const
类内的变量是否为const?
指针的双重const, 在什么之前就修饰什么, 指针为const, 还是指针指向的值为const, 常量指针
const char* const authorName = "Quincy"
1函数的参数是否传const引用? 使用const修饰, 会明示参数是否被改变(引用不改变, 要加const表示出来);
成员函数后是加const, 代表这个成员函数不会修改类内的值;
返回值是否要成为const(为什么?), 看返回值类型是否是需要const不允许变动(防止用户自身的村务使用, 不放弃高效性), 其次是避免成为左值被修改;
a.get_data()=3; // 返回一个const, 这种操作就是不合法的;
mutable可以释放掉non-static成员变量的bitwise constness约束;
class CTextBlock{ public: std::size_t length() const; private: char* pText; mutable std::size_t textLength; //这些变量可能总是会被改变 mutable bool lengthIsValis; // 在length()中, 会频繁改变上述的两个变量; }
1
2
3
4
5
6
7
8
9Key:
将某些东西声明为const可帮助编译器提示出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)。
当const和non-const成员函数有着实质等价的实现时(确实是要有两个版本),令non-const版本调用const版本可避免代码重复。因为const可以兼容non-const的输入, 但是non-const不可以兼容const的输入
class TextBlock { public: ... const char& operator[](std::size_t position) const // 表示不改变类成员 { ... // do bounds checking ... // log access data ... // verify data integrity return text[position]; } char& operator[](std::size_t position) { ... // do bounds checking ... // log access data ... // verify data integrity return text[position]; } private: std::string text; };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Item 04:Make sure that objects are initialized before they're used. 对象在使用前已先被初始化
没有初始化的值,在读它的时候会导致不明确的行为, 变量里面包含的内容都是半随机状态;
在初始化列表里面, 列出所有的成员变量, 免得遗漏, 除非记得住哪些不用初始值;
- 实在太多的话, 用一个private函数包一下能够赋值处理的, 也可以;
- 初始化的顺序总是按照声明顺序来, 不是按照初始化列表的顺序
成员变量是const或者reference, 就一定要初始化, 因为后面不能再被赋值了;
如果有一个non-local static对象, 没有办法保证是否使用的时候已经初始化, 那就在本地搞一个static函数(处理static对象的函数叫做static的函数)把这个对象包起来,返回一个指向这个对象的reference, 这样, 外部的static对象就变成了本地的static对象, 这也是singleton模式的一个常见实现手法(这种手法叫做reference returning)
Directory& tempDir(){ static Directory& td; // more effective c++里面, 限制对象数目的方式, 好像是一样的 return td; // 还是把non-loca的static的对象,改变成local static的对象比较好; }
1
2
3
4任何一种non const static 对象,在多线程环境下, 都会有线程安全的问题;non const代表了可以被改变, 多线程可以同时改变它;