不要包含不必要的头文件
尽量使用前向声明的方式,目的是为了减少编译时间What are forward declarations in C++?,并且在头文件发生改变的时候,减少重新编译的文件。
将内部类移动到实现中
// 内部类的声明class Whatever { public: /* ... */ PRivate: struct DataStruct; std::vector<DataStruct> data_;};struct Whatever::DataStruct {};某些时候你不能这么做,因为某些STL数据结构,需要在声明的时候就知道其定义,比如:std::deque
不使用这个接口的人是不需要关心BigImplementationDetail
实现细节的。使用匿名空间的好处就是可以限制对象的作用域在本文件中。
停止对虚方法进行内联
在大多数情况不要对一个虚方法进行内联,即使是因为这些方法很短也是不可以的,编译器必须在运行时根据虚函数表和指向的类型做分发,如果内联了就无法知道指向的类型。
Can virtual functions be inlined?
Are inline virtual functions really a non-sense?
停止内联构造和析构函数
构造函数和析构函数要比我们想象中的要复杂,因为编译器会帮我们插入很多初始化的代码,因此不要认为构造函数和析构函数中没写代码就可以内联。
什么情况下可以有构造函数和析构函数
如果你的类只有POD类型的数据,并且没有显示的声明析构函数,那么编译器也不会生成trivial destructor。
struct Data { Data() : count_one(0), count_two(0) {} // No explicit destructor, thus no implicit destructor either. // The members must all be POD for this trick to work. int count_one; int count_two;}; 没有继承,只有很少一些POD类型,构造函数都是一些trivial的整型操作,所以是可以内联的。对于抽象类, 并且没有成员的情况,它是可以安全内联一个trivial的析构函数
class Interface { public: virtual ~Interface() {} virtual void DoSomething(int parameter) = 0; virtual int GetAValue() = 0; }; 下面两个接口,不能进行inline。
class ClaimsToBeAnInterface : public base:RefCounted<ClaimsToBeAnInterface> { public: virtual ~ClaimsToBeAnInterface() { /* But derives from a template! */ }};class HasARealMember { public: virtual void InterfaceMethod() = 0; virtual ~HasARealMember() {} protected: vector<string> some_data_;};小心你的访问器
不是所有的访问器都是轻量级的
class Foo { public: int count() const { return count_; } private: int count_;}; 上面这个访问器是trivial的,可以很安全的inline,但是下面这些代码就不行了,即使它们看起来很像。
struct MyData { vector<GURL> urls_; base::Time last_access_;};class Manager { public: MyData get_data() { return my_data_; } private: MyData my_data_;}; MyData底层的数据拷贝很复杂,所以不适合inline
TODO
动态初始化函数作用域内的静态变量在C++11
中是线程安全的,因此base::LazyInstance
被广泛使用,
在C++11中有很多方式可以用来初始化一个变量,优先遵从下面这些规则:
对于简单变量的初始化,以及多个literal value组成一个对象的初始化使用赋值语法int i = 1;std::string s = "Hello";std::pair<bool, double> p = {true, 2.0};std::vector<std::string> v = {"one", "two", "three"};这里使用=
号并不会没有()
效率高,因为这里编译器不会生成临时变量进行拷贝,并且确保只有隐式的构造函数被调用,因此读者在看到这种调用方式可以假设没有什么复杂或微妙的事情发生。
base::MakeUnique(…) 和 base::WrapeUnique(new Type())是等同的,MakeUnique更好,因为通常来说用它很难写出不安全的代码。
return std::unique_ptr<C>(new C(1, 2, 3)); // BAD: type name mentioned twice return base::WrapUnique(new C(1, 2, 3)); // BAD: bare call to new return base::MakeUnique<C>(1, 2, 3); // GOOD注意: MakeUnique就是C++14中的make_unique的实现
不要将MakeUnique设置为类的友元,因为这会让任何人都可以构造这个类 class Bad { public: std::unique_ptr<Bad> Create() { return base::MakeUnique<Bad>(); } // ... private: Bad(); // ... friend std::unique_ptr<Bad> base::MakeUnique<Bad>(); // Lost access control};class Okay { public: // For explanatory purposes. If Create() adds no value, it is better just // to have a public constructor instead. std::unique_ptr<Okay> Create() { return base::WrapUnique(new Okay()); } // ... private: Okay(); // ...}; 对于WrapUnique(new Foo) 和 WrapUnique(new Foo()) 来说,如果Foo没有自定义的构造函数的话那么这两者的含义是不同的,不要让未来的维护者猜测你是否是故意不写(),使用MakeUnique()来替换,如果你是有意不写()来作为优化,请加上说明 auto a = base::WrapUnique(new A); // BAD: "()" omitted intentionally? auto a = base::MakeUnique<A>(); // GOOD // "()" intentionally omitted to avoid unnecessary zero-initialisation. // WrapUnique() does the wrong thing for array pointers. auto array = std::unique_ptr<A[]>(new A[size]); 鼓励使用auto来从初始化表达式中进行类型推导,但是当推导的是指针类型的时候不要使用auto,这会给使用者带来疑惑,应该使用auto*来替代,如下:
auto item = new Item(); // BAD: auto deduces to Item*, type of |item| is Item*auto* item = new Item(); // GOOD: auto deduces to Item, type of |item| is Item*新闻热点
疑难解答
图片精选