<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. laravel form 表单提交

    form表单需要加token,不然会出现419错误,csrf_token不用自己生成,放进去就行,laravel自己会生成 路由: 控制器生成一个:

  2. 7z 压缩解压简单示例

    7z命令行压缩示例: 7z -tZip a test.zip ./test/* -mx0 把test文件夹中所有文件以存储压缩的模式压缩成zip格式的文件,压缩文件为test.zip a为添加选项   ...

  3. telent connection refused

    1.问题场景 Centos7 做flume案例时,telnet hadoop-senior03.itguigu.com 44444 总是Connection redused, Trying 192.1 ...

  4. FIFO队列 ADT接口 链表实现

    FIFO.h (接口) #include "Item.h" void QUEUinit(int); int QUEUempty(void); void QUEUput(Item); ...

  5. go基础语法-常量与枚举

    1.常量定义 用const关键字修饰常量名并赋值,常量命名不同于java等语言,golang中一般用小写,因为在golang中首字母大写表示public权限 const a = 3 2.常量使用 使用 ...

  6. 北京Uber优步司机奖励政策(2月29日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. Mac OS下Android Studio:/dev/kvm not found

    在配置模拟器时出现该报错,在网上找了很多教程都没能解决,当然可能是这些教程并不适用于我.总的来说,还是要“对症下药”! 解决方法如下: 点击“系统偏好设置”-“安全性与隐私”,然后会在“通用”这个界面 ...

  8. JavaSE基础笔记

    JVM 不是跨平台的,他是用 C++编写的. Path 环境变量的地址是 ...jdk/bin java_home 地址是 ...jdk

  9. 11gR2RAC更换CRS磁盘组文档

    磁盘(pv)准备     在生产环境中,提前从存储上划分一些磁盘挂载到RAC系统的两个节点上(node1,node2).     新增加磁盘组为(hdisk14--hdisk24) 1.1磁盘使用规划 ...

  10. jmeter开发自己的sampler插件

    1. 新建maven工程 2.pom文件引入jmeter的核心包 <project xmlns="http://maven.apache.org/POM/4.0.0" xml ...