<Item 9> Never call virtual functions during construction or destruction

1、you shouldn't call virtual functions during construction or destruction, because the calls won't do what you think, and if they did, you'd still be unhappy. If you're a recovering Java or C# programmer, pay close attention to this Item, because this is a place where those languages zig, while C++ zags.java可以在构造函数中调用子类函数,但是子类成员变量此时没有初始化。

2、During base class construction, virtual functions never go down into derived classes. Instead, the object behaves as if it were of the base type. Informally speaking, during base class construction, virtual functions aren't.

3、It's actually more fundamental than that. During base class construction of a derived class object, the type of the object is that of the base class. Not only do virtual functions resolve to the base class, but the parts of the language using runtime type information (e.g., dynamic_cast (see Item 27) and typeid) treat the object as a base class type. An object doesn't become a derived class object until execution of a derived class constructor begins.

Upon entry to the base class destructor, the object becomes a base class object, and all parts of C++ — virtual functions, dynamic_casts, etc., — treat it that way.

4、不小心在构造函数或者析构函数中间接调用虚函数是C++的常见错误,因此最好在编码规范中严格进行限制。

class Transaction {

public:
Transaction()
{ init(); } // call to non-virtual... virtual void logTransaction() const = ;
... private: void init()
{
...
logTransaction(); // ...that calls a virtual!
}
};

可以在派生类中定义必要的static函数向基类构造函数传递必要的信息

class Transaction {

public:
explicit Transaction(const std::string& logInfo);
void logTransaction(const std::string& logInfo) const; // now a non-
// virtual func
...
}; Transaction::Transaction(const std::string& logInfo)
{
...
logTransaction(logInfo); // now a non-
} // virtual call class BuyTransaction: public Transaction {
public:
BuyTransaction( parameters )
: Transaction(createLogString( parameters )) // pass log info
{ ... } // to base class
... // constructor
private:
static std::string createLogString( parameters );
};

Using a helper function to create a value to pass to a base class constructor is often more convenient (and more readable) that going through contortions in the member initialization list to give the base class what it needs.

5、Things to Remember

  • Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor.

<Item 10>Have assignment operators return a reference to *this

6、让=、+=、-=、*=等赋值运算符的重载函数return *this,这样可以实现类似built-in变量的功能   code that doesn't follow it will compile. However, the convention is followed by all the built-in types as well as by all the types in (or soon to be in — see Item54) the standard library (e.g., string, vector, complex, tr1::shared_ptr, etc.). Unless you have a good reason for doing things differently, don't.

int x, y, z;
x = y = z = ; // chain of assignments

7、Things to Remember

  • Have assignment operators return a reference to *this.

<Item 11> Handle assignment to self in operator=

8、通过数组循环、指针引用、基类和派生类的指针混用,很容易导致对象自己向自己赋值。如下使用对象管理资源的代码,自我赋值的时候就会导致资源被错误的delete

class Bitmap { ... };

class Widget {
... private:
Bitmap *pb; // ptr to a heap-allocated object
};
Widget& Widget::operator=(const Widget& rhs)              // unsafe impl. of operator=

{
delete pb; // stop using current bitmap
pb = new Bitmap(*rhs.pb); // start using a copy of rhs's bitmap return *this; // see Item 10
}

9、传统的保护方式如下,这种方法解决了保护了自我赋值的问题,但是还会有另外一个异常安全问题,假如new Bitmap(*rhs.pb)抛出异常,会导致pb被指向一个被delete的bitmap对象

Widget& Widget::operator=(const Widget& rhs)
{
if (this == &rhs) return *this; // identity test(证同测试): if a self-assignment,
// do nothing delete pb;
pb = new Bitmap(*rhs.pb);
return *this; }

a careful ordering of statements can yield exception-safe (and self-assignment-safe) code

Widget& Widget::operator=(const Widget& rhs)
{ Bitmap *pOrig = pb; // remember original pb
pb = new Bitmap(*rhs.pb); // make pb point to a copy of *pb
delete pOrig; // delete the original pb
return *this;
}

If you're concerned about efficiency, you could put the identity test back at the top of the function. Before doing that, however, ask yourself how often you expect self-assignments to occur, because the test isn't free. It makes the code (both source and object) a bit bigger, and it introduces a branch into the flow of control, both of which can decrease runtime speed. The effectiveness of instruction prefetching, caching, and pipelining can be reduced, for example.

10、An alternative to manually ordering statements in operator= to make sure the implementation is both exception- and self-assignment-safe is to use the technique known as "copy and swap." This technique is closely associated with exception safety, so it's described in Item 29. However, it's a common enough way to write operator= that it's worth seeing what such an implementation often looks like

class Widget {
...
void swap(Widget& rhs); // exchange *this's and rhs's data;
... // see Item 29 for details
}; Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs); // make a copy of rhs's data
swap(temp); // swap *this's data with the copy's return *this;
}

A variation on this theme takes advantage of the facts that (1) a class's copy assignment operator may be declared to take its argument by value and (2) passing something by value makes a copy of it (see Item 20):

Widget& Widget::operator=(Widget rhs)   // rhs is a copy of the object
{ // passed in — note pass by val
swap(rhs); // swap *this's data with
// the copy's
return *this;

Personally, I worry that this approach sacrifices clarity at the altar of cleverness, but by moving the copying operation from the body of the function to construction of the parameter, it's a fact that compilers can sometimes generate more efficient code.

11、Things to Remember

  • Make sure operator= is well-behaved when an object is assigned to itself. Techniques include comparing addresses of source and target objects, careful statement ordering, and copy-and-swap.

  • Make sure that any function operating on more than one object behaves correctly if two or more of the objects are the same.

<Item 12>Copy all parts of an object

12、In well-designed object-oriented systems that encapsulate the internal parts of objects, only two functions copy objects: the aptly named copy constructor and copy assignment operator. We'll call these the copying functions. Item 5 observes that compilers will generate the copying functions, if needed, and it explains that the compiler-generated versions do precisely what you'd expect: they copy all the data of the object being copied.

13、 if you add a data member to a class, you need to make sure that you update the copying functions, too. (You'll also need to update all the constructors (see Items 4 and 45) as well as any non-standard forms of operator= in the class (Item 10 gives an example). If you forget, compilers are unlikely to remind you.)

14、One of the most insidious ways this issue can arise is through inheritance.Instead, derived class copying functions must invoke their corresponding base class functions:

void logCall(const std::string& funcName);          // make a log entry

class Customer {
public:
...
Customer(const Customer& rhs);
Customer& operator=(const Customer& rhs);
... private:
std::string name;
};
class PriorityCustomer: public Customer {                  // a derived class
public:
...
PriorityCustomer(const PriorityCustomer& rhs);
PriorityCustomer& operator=(const PriorityCustomer& rhs);
... private:
int priority;
};
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
: Customer(rhs), // invoke base class copy ctor
priority(rhs.priority)
{
logCall("PriorityCustomer copy constructor");
} PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
logCall("PriorityCustomer copy assignment operator"); Customer::operator=(rhs); // assign base class parts
priority = rhs.priority; return *this;
}

假如没有红色代码处理基类数据,复制构造函数将会调用基类的默认构造函数初始化基类的变量,如果没有默认构造函数,代码不会编译通过,复制赋值操作符将会不会对基类数据作操作,基类数据保持不变。

15、When you're writing a copying function, be sure to(1) copy all local data members and (2) invoke the appropriate copying function in all base classes, too.

16、copy assignment operator和copy constructor代码相似但是不能相互调用,因为一个是改变已有对象,一个是创建新对象。Instead, if you find that your copy constructor and copy assignment operator have similar code bodies, eliminate the duplication by creating a third member function that both call. Such a function is typically private and is often named init. This strategy is a safe, proven way to eliminate code duplication in copy constructors and copy assignment operators.

17、Things to Remember

  • Copying functions should be sure to copy all of an object's data members and all of its base class parts.

  • Don't try to implement one of the copying functions in terms of the other. Instead, put common functionality in a third function that both call.

<Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<二>的更多相关文章

  1. <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<一>

    <Item 5> Know what functions C++ silently writes and calls 1.If you don't declare them yoursel ...

  2. <Effective C++>读书摘要--Designs and Declarations<一>

    <Item 18> Make interfaces easy to use correctly and hard to use incorrectly 1.That being the c ...

  3. <Effective C++>读书摘要--Implementations<一>

    1.For the most part, coming up with appropriate definitions for your classes (and class templates) a ...

  4. <Effective C++>读书摘要--Accustoming Youself to C++

    <Item 1>View C++ as a federation of languages. 1.把C++看成4种子语言组成,即C.Object-Oriented C++.Template ...

  5. <Effective C++>读书摘要--Introduction

    Introduction 1.Learning the fundamentals of a programming language is one thing; learning how to des ...

  6. <Effective C++>读书摘要--Templates and Generic Programming<一>

    1.The initial motivation for C++ templates was straightforward: to make it possible to create type-s ...

  7. <Effective C++>读书摘要--Inheritance and Object-Oriented Design<二>

    <Item 36> Never redefine an inherited non-virtual function 1.如下代码通过不同指针调用同一个对象的同一个函数会产生不同的行为Th ...

  8. <Effective C++>读书摘要--Implementations<二>

    <Item29> Strive for exception-safe code. 1.如下面的代码 class PrettyMenu { public: ... void changeBa ...

  9. <Effective C++>读书摘要--Designs and Declarations<三>

    <Item 22> Declare data members private 1.使数据成员private,保持了语法的一致性,client不会为访问一个数据成员是否需要使用括号进行函数调 ...

随机推荐

  1. Hibernate-关系映射

    1.为什么用Hibernate框架: java程序数据保存的变化: * 内存存在:java基础中, 数据保存在内存中,只在内存中暂时存在 * 文件保存:有io/流之后,数据可以保存到文件中 * 数据库 ...

  2. BZOJ1053_反素数_KEY

    题目传送门 初看这道题,以为是一道挺难的题目,但仔细看发现,不是只要爆搜就好了吗? 只需要对前12个素数进行爆搜即可. 一个数的因数个数=素数次数+1全部乘起来. code: /*********** ...

  3. <简明>Markdown指南

    什么是Markdown?Markdown是一种轻量级的「标记语言」,通常为程序员群体所用,目前它已是全球最大的技术分享网站 GitHub 和技术问答网站 StackOverFlow 的御用书写格式. ...

  4. LeetCode: 55. Jump Game(Medium)

    1. 原题链接 https://leetcode.com/problems/jump-game/description/ 2. 题目要求 给定一个整型数组,数组中没有负数.从第一个元素开始,每个元素的 ...

  5. Habse中Rowkey的设计原则——通俗易懂篇

    Hbase的Rowkey设计原则 一. Hbase介绍 HBase -> Hadoop Database,HBase是Apache的Hadoop项目的子项目.HBase不同于一般的关系数据库,它 ...

  6. 11、Java并发编程:并发容器之CopyOnWriteArrayList

    Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...

  7. DSP5509开发之FPGA接口

    1. DSP5509和FPGA或者CPLD之间是什么接口,DSP相对普通MCU,具有专门的硬件乘法器,程序和数据分开的哈弗结构,特殊的DSP指令,快速的实现各种数字信号处理算法.在一个周期内可以完成一 ...

  8. Android 模拟器 下载、编译及调试

    Android 模拟器源码下载 Android 模拟器源码的下载与 Android AOSP 源码库的下载过程类似,可以参考 Google 官方提供的 Android 源码下载文档 来了解这个过程. ...

  9. hdu2553N皇后问题(dfs,八皇后)

    N皇后问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  10. OSG-CompositeViewer

    原文连接地址:http://www.osgchina.org/index.php?Itemid=490&id=134:usecompositiv&option=com_content& ...