<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. Linux awk基础笔记

    正则表达式含义与构成:用单个字符串来描述或者匹配一系列符合某个句法规则的字符串,一般是由普通字符与特殊字符组成 awk 选项 '模式或者命令{编辑指令}' 文件名 awk '//{print}' aw ...

  2. php源码建博客1--搭建站点-实现登录页面

    主要: 站点搭建 实现登录页面 分析及改进 站点搭建 1)  在apache安装目录下: [conf\extra\httpd-vhosts.conf]加入站点配置 <VirtualHost *: ...

  3. STL---llist

    #include<iostream> #include<algorithm> #include<list> using namespace std; struct ...

  4. python--re模块(正则表达式)

    RE是什么 正则 表达 式子 就是一些带有特殊含义的符号或者符号的组合 它的作用是对字符串进行过滤 在一堆字符串中找到你所关心的内容 你就需要告诉计算机你的过滤规则是什么样 通过什么方式来告诉计算机 ...

  5. Go复习

    # 代码包 #命名基础包 package “base” #导入基础包 import( "base1" ba "base2" 只导入当不使用情况下需要添加别名 . ...

  6. Java-谈谈对Java平台的理解

    问题 谈谈对 Java 平台的理解 Java是解释执行的 这句话对么 程序的编译与解释有什么区别 Java 平台的了解 Java的主要特点是两个, 编写一次到处运行 Write once, run a ...

  7. 20145226夏艺华《网络对抗》第一次实验拓展:shellcode注入+return-to-libc

    20145226夏艺华<网络对抗>第一次实验拓展:shellcode注入+return-to-libc shellcode注入实践 编写shellcode 编写shellcode已经在之前 ...

  8. 北京Uber优步司机奖励政策(高峰期5倍奖励)(12月7日)

    用户组:人民优步及电动车(适用于12月7日) 滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http ...

  9. 【SQLSERVER】从数据库文件mdf中拆分ndf的方法和利弊

    一.数据文件格式 SQLSERVER中,数据库的文件后缀有3种:mdf.ndf.ldf. 如下图所示,DW_TEST.mdf.DW_TEST_HIS.ndf.DW_TEST.ldf 属于同一个数据库T ...

  10. spring源码-国际化-3.5

    一.国际化在实际代码中是非常常见的一中方式.为了结合web做一下语言上面的切换,而达到展示的目的. 二.这里呢,主要是介绍spring中对于国际化做了哪些处理. 三.实现方式 1)xml配置 < ...