Effective C++ 总结
来源:互联网 发布:java jdbc 连接 编辑:程序博客网 时间:2024/06/05 16:33
第三版
Rule 3: 尽可能的使用const
- const可以作用于作用于内的对象、函数参数、函数返回值、成员函数体
- 可以使用const进行函数重载
- 对“成员函数为const”的两种观点:bitwise constness 和 logical constness
- bitwise constness: const成员函数绝不改变任何成员变量(static除外)时才可以说时const。C++对“常量性”的定义就是如此。(但是,想想一个类种有指针成员变量的情况,char * text,有可能成员函数没有改变指针本身的值,但是指针指向内容确可以被意外改变,详见书中的例子)
- logical constness:const成员函数可以改变对象的成员变量,但是不能让客户端侦测出来
- mutable(可变的) 能够释放掉non-static成员变量的bitwise constness约束,使得该成员变量可以在const成员函数中被修改。
- 在const和non-const成员函数中避免重复(避免代码重复等问题),当const和non-const成员函数有实质等价的实现时,令non-const版本调用const版本可以避免代码重复。
Rule 4: 确定对象被使用前已经被初始化
- 注意初始化和赋值之间的区别!初始化位于构造函数的函数体之前的部分,称为构造函数的初始化列表(此处进行初始化),构造函数的函数体内使用“=”为赋值。初始化列表更高效!
- c++对“定义于不同的编译单元内的non-local static对象”的初始化次序没有明确定义。使用 local static对象代替non-local static 对象。
- 对内置类型对象,要手工初始化,因为c++不保证初始化它们。
- 构造函数最好使用成员初始化列表,而不要在构造函数内部使用赋值操作(assignment)。初始化列表中成员的顺序要与class中声明次序保持一致(良好的习惯)。
- copy constructor : 拷贝构造函数。会在以下三种情况下被调用:
1.一个对象以值传递的方式传入函数体
2.一个对象以值传递的方式从函数返回
3.一个对象需要通过另外一个对象进行初始化。
A aa(a); 或 A aa=a; 都会调用拷贝构造函数。注意A aa=a; 不是调用 copy assignment(operator=) ,原因是这是个定义,不是个赋值。但是如果是A aa; aa=a;,第二个语句会调用operator=,这里是赋值。
//Singleton 模式实现的一种方法static CApplication & Instance(){ // local static,函数第一次被调用时构造。以后的调用直接返回。实现了单例模式。 static CApplication appliction; return application;//返回local static的引用}
注意local-static的线程安全性。C++11能确保local-static的线程安全性。此外,使用pthread_once
也可以实现线程安全的Singleton模式。
Rule 20: Prefer to Pass-by-const-reference to Pass-by-value
参数传递时,使用const reference 代替 pass-by-value(值传递)。
值传递的缺点:
额外的构造和析构
包括对象本身以及对象的类类型成员变量,如果是派生类,那么基类的构造析构也会调用。对象切割问题(slicing)
如果使用pass-by-value传递参数,但把一个派生类对象传递给基类类型参数时,会发生对象切割,只有基类的部分被正确传递。这是需要使用引用。(引用在底层是用指针来实现的,对于内置类型、STL迭代器、函数对象,使用pass-value比较合理。)
此外,C++ FAQ 指出,return-by-value经过编译器的优化,不会有额外的copy等开销。
Rule 25: Consider support non-throwing swap
std::swap用于交换两个对象值,只要类型T支持Copying (copying ctor 和 Copy assign)即可。实现细节类似这样:
T tmp(a);a = b;b = a;
进行了一次copy ctor,和两次copy assign。
这种行为在某些场景下比较低效。例如使用“pimpl”手法设计的类(pointer to implementation),通常我们只是希望交换指针的内容而已。但是如果使用缺省的std::swap却会进行一些不必要的操作(注意Widget::operator=, 他会调用WidgetImpl的operator=, 这会复制它的所有成员,包括两个int和一个vector,非常低效!)
文中给出了第一个解决办法:将std::swap针对Widget特化:
class WidgetImpl { friend ostream& operator<< (ostream& os, const WidgetImpl&);public: WidgetImpl(int a, int b) : a_(a), b_(b) { v_.push_back(a); v_.push_back(b); }private: int a_, b_; std::vector<double> v_;};class Widget { friend ostream& operator<< (ostream& os, const Widget& w); friend void swap<Widget>(Widget&, Widget&); // 声明特化版本的swap为友元函数public: Widget(WidgetImpl* p) : pimpl_(p) {} Widget& operator= (const Widget& rhs) { std::cout << "operator=" << std::endl; *pimpl_ = *rhs.pimpl_; return *this; }private: WidgetImpl *pimpl_;};// 可以对std内的模板函数进行特化namespace std { template<> // 表示这是一个全特化 void swap<Widget>(Widget& a, // <Widget>表示针对Widget进行特化 Widget& b) { swap(a.pimpl_, b.pimpl_); // 需要访问Widget的私有成员,因此在上面声明为friend函数。 }}ostream& operator<< (ostream& os, const WidgetImpl& w){ os << w.a_ << " " << w.b_ << std::endl; return os;}ostream& operator<< (ostream& os, const Widget& w){ os << *w.pimpl_ << std::endl; return os;}int main(){ WidgetImpl w1(1,2), w2(3,4); //std::cout << w1 << w2; Widget pw1(&w1), pw2(&w2); swap(pw1, pw2); std::cout << pw1 << pw2; return 0;}
第二种方法,在特化版本的swap中调用Widget的成员函数,这样无需声明friend函数。改动如下所示:
void Widget::swap(Widget& rhs){ using std::swap; swap(pimpl_, rhs.pimpl_);}namespace std { template<> void swap<Widget>(Widget& a, Widget& b) { //swap(a.pimpl_, b.pimpl_); a.swap(b); }}
这样的做法与STL容器有一致性,所有STL容器也提供有public swap函数,和std::swap特化版本(用来调用前面的public swap函数)。
Rule 30: Understand “inline”
- inline 只是对编译器的一个申请,不是强制命令。它可以被显示地提出(使用inline关键字),也可以隐喻地提出(将函数定义写在class内)。
Rule 42: Understand two meanings of typename
- 声明template参数时,关键字
typename
和class
等价。 - 应该使用typename标识嵌套从属类型名称;但是不得在 base class list 和 member initialize list 内以typename作为base class的修饰符。
template<typename Container>void print(const Container& c){ typename Container::const_iterator cit; // const_iterator 是个嵌套从属类型名称,需要在前面加上typename cit = c.begin(); std::cout << *cit << std::endl;}
- 《Effective C++》总结
- 《Effective C++》学习总结
- 《Effective C++》02总结
- Effective obj-C 2.0 总结
- Effective Objective-C 2.0 总结
- 《Effective C++》条款03总结
- 《Effective C++》条款04总结
- 《Effective C++》条款07总结
- 《Effective C++》与《More Effective C++》笔记总结
- 《Effective C#》Part I:第一部分总结
- 《Effective C#》Part II:第二部分总结
- 《Effective Modern C++》Item 1总结
- 《Effective Modern C++》Item 2总结
- Effective Modern C++》Item 3总结
- Effective objective-C 2.0总结归纳
- Effective Objective-C 2.0总结(第一章)
- 读<<effective C++>>的疑惑总结
- 《Effective C++》关于const,define等总结
- LeetCode 009 Palindrome Number
- MOV指令
- 关于CPU Cache和Cache Line
- Eclipse:An error occurred while automatically activating bundle com.android.ide.eclipse.adt(6)
- 用Canvas画Switch控件
- Effective C++ 总结
- maven scope含义的说明
- BEM命名
- mysql-5.6.24设置数据库编码
- 7.6 鼠标的滚轮
- MFC下的键盘响应代码
- ibeacon微信摇一摇周边应用平台cloud.i-io2o.com
- Javascript之闭包理解
- Android Volley(四)自定义各种request