Effective C++ 笔记 第二部分 构造/析构/赋值运算
来源:互联网 发布:认识数据库管理 编辑:程序博客网 时间:2024/06/11 09:23
5.了解C++默默编写并调用哪些函数(know what functions C++ silently writes and calls)
编译器可以暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数。
编译器拒绝提供copy assignment操作符的2种情况:
1.有reference成员变量
2.有const成员变量
因为reference/const成员变量是不可赋值的,所以需要手动提供copy assignment操作符。
6.若不想使用编译器自动生成的函数,就改明确的拒绝(Explicitly disallow the use of compiler-generated functions you do not want)
为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。像使用Uncopyable这样的base class也是一种做法
拒绝编译器提供自动生成函数的两种方法
将要拒绝提供的函数声明为private并不予实现(因为friend和member函数可以调用private函数)。
class HomeForSale{private: HomeForSale(const HomeForSale&); HomeForSale& operator=(const HomeForSale&);};
使类继承自一个使用上述方法基类,使编译器无法提供要被拒绝的函数
class Uncopyable{private: Uncopyable(const Uncopyable&); Uncopyable& operator=(const Uncopyable&);};class HomeForSale: private Uncopyable{};
7.为多态基类声明virtual析构函数(Declare destructors virtual in polymorphic base classes.)
polymorphic(带有多态性质的)base class 应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数。
如果derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义。
解决方法是在base class中添加virtual析构函数。使得通过base class指针可调用derived class的析构函数。
同理我们的base class中如果有virtual function也应该声明virtual析构函数。因为derived class中vptr删除等操作应客制化。
Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数。因为会安插vptr增大类的体积,丧失可移植性。
8.别让异常逃离析构函数(Prevent exceptions from leaving destructors)
析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下他们(不传播)或结束程序。
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非析构函数中)执行该操作。
9.绝不在构造和析构过程中调用virtual函数(Never call virtual functions during construction or destruction.)
在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)
若base class在构造期间调用virtual函数,则其derived class在构造期间调用的base class构造函数中调用的该virtual函数是base class版本的,因为在构造期间,base class部分先于derived class部分构造完成,当base class的构造函数调用virtual函数时,derived class尚未形成,所以调用的是base class版本的virtual函数。析构函数同理。
class Base{public: Base(){ virFunc(); } virtual void virFunc() const { printf("base class virtual function\n"); }};class derived: public Base{public: virtual void virFunc() const { printf("derived class virtual function\n"); }};int main(int argc, const char * argv[]) { derived d; //输出:base class virtual function return 0;}
10.另operator= 返回一个reference to *this(Have assignment operators return a reference to *this)
返回一个reference to *this将支持连续赋值:例如a = b = c;
class A{public: A(int val):val(val){} A& operator=(const A& rhs){//返回reference to *this this->val = rhs.val; return *this; } int val;};int main(int argc, const char * argv[]) { A a1(1); A a2(2); A a3(3); a1 = a2 = a3; std::cout<<"a1="<<a1.val<<" a2="<<a2.val<<" a3="<<a3.val<<std::endl; return 0;}输出:a1=3 a2=3 a3=3
同理,不止operator=需要返回reference to *this,例如+=,-=等操作符也该返回reference to *this。
11.在operator=中处理”自我赋值”(Handle assignment to self in operator=)
确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然确定。
自我赋值应考虑异常安全性和自我赋值安全性。如下版本的自我赋值是不安全的。
class Data{};class A{public: A& operator=(const A& rhs){ delete data;//如果rhs与this是同一个对象,this将指向已删除对象。 data = new Data(*rhs.data);//如果此处发生了异常,this将指向已删除的对象。 return *this; }private: Data* data;};
解决方法是将this做一个副本,再将传入参数copy给this,在删除副本。
A& operator=(const A& rhs){ Data* temp = data; data = new Data(*rhs.data);//如果此处发生异常,this的data不会消失。即使this与rhs指向同一对象也不会发生错误。 delete temp; return *this; }
12.赋值对象时勿忘其每一个成分(Copy all parts of an object)
Copying函数应该确保赋值“对象内的所有成员变量”及”所有base class成分”
不要尝试以某个copying函数实现另一个copying函数。应该将共有机能放进第三个函数中,并由两个copying函数共同调用。(不要以copy assignment调用copy constructor,反之亦然。但可以用copy constructor调用copy constructor,copy assignment同样)
自定义copying函数(包含copy构造函数和operator=和类似的函数)时要考虑每一个成员的操作,编译器不会产生警告信息或为你添加copy成员的语句。
在derived class中的自定义copying函数中要调用base class的对应copying函数(由编译器合成copying函数的会由编译器调用),因为编译器同样不会为你复制继承自base class的成员变量。写法应类似下边这样:
Derived(const Derived& rhs) :Base(rhs) { //do something; }
用copy assignment调用copy 构造函数是不合理的,因为这就像试图构造一个已经存在的对象。用copy构造函数调用copy assignment也是无意义的,若两个copying函数有共同点应把共同之处写在第三个函数中,再由两个copying函数调用。
- Effective C++ 笔记 第二部分 构造/析构/赋值运算
- Effective C++读书笔记 第二部分 构造/析构/赋值运算
- 《Effective C++》读书笔记(三) 构造/析构/赋值运算 (第二部分)
- 《Effective C++》第二章:构造/析构/赋值运算
- Effective C++(二)构造/析构/赋值运算
- effective c++-构造/析构/赋值运算
- 《Effective C++》构造、析构、赋值运算
- <Effective C++ : 构造/析构/赋值运算> 笔记
- 《Effective C++》读书笔记(二) 构造/析构/赋值运算 (第一部分)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之一)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之二)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之三)
- 【读书笔记】Effective C++-2 构造/析构/赋值运算(之四)
- Effective C++(二)构造/析构/赋值运算
- Effective C++笔记: 构造/析构/赋值运算(一)
- Effective C++笔记: 构造/析构/赋值运算(二)
- Effective C++学习笔记二(构造/析构/赋值运算)
- Effective C++ 笔记二构造/析构/赋值运算
- 相似数据检测算法(shingle,SimHash,Bloomfilter) 比较
- 20条Linux命令面试问答
- IT人生
- 《剑指offer》——构建乘积数组
- java提取字符串中的汉字
- Effective C++ 笔记 第二部分 构造/析构/赋值运算
- leetcode-Permutations
- Unity-APk 错误:Error building Player: Couldn't build player because of unsupported data on target plat
- android中handle的用法
- char数组与char指针的区别与联系
- iOS获取汉字首字母
- 做一个有价值的软件开发者
- python IDE:PyCharm简介
- iOS获取汉字首字母