<Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<二>
<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<二>的更多相关文章
- <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 ...
- <Effective C++>读书摘要--Designs and Declarations<一>
<Item 18> Make interfaces easy to use correctly and hard to use incorrectly 1.That being the c ...
- <Effective C++>读书摘要--Implementations<一>
1.For the most part, coming up with appropriate definitions for your classes (and class templates) a ...
- <Effective C++>读书摘要--Accustoming Youself to C++
<Item 1>View C++ as a federation of languages. 1.把C++看成4种子语言组成,即C.Object-Oriented C++.Template ...
- <Effective C++>读书摘要--Introduction
Introduction 1.Learning the fundamentals of a programming language is one thing; learning how to des ...
- <Effective C++>读书摘要--Templates and Generic Programming<一>
1.The initial motivation for C++ templates was straightforward: to make it possible to create type-s ...
- <Effective C++>读书摘要--Inheritance and Object-Oriented Design<二>
<Item 36> Never redefine an inherited non-virtual function 1.如下代码通过不同指针调用同一个对象的同一个函数会产生不同的行为Th ...
- <Effective C++>读书摘要--Implementations<二>
<Item29> Strive for exception-safe code. 1.如下面的代码 class PrettyMenu { public: ... void changeBa ...
- <Effective C++>读书摘要--Designs and Declarations<三>
<Item 22> Declare data members private 1.使数据成员private,保持了语法的一致性,client不会为访问一个数据成员是否需要使用括号进行函数调 ...
随机推荐
- ASP.NET Core学习网站推荐
跟大家推荐一个不错的学习.NET Core 的网站,这个网站的视频是付费的,但是录视频的都是.NET Core的大佬们,个人觉得很不错推荐出来 video.jessetalk.cn
- 学习新框架laravel4 第三天
请求与输入 获取请求参数 如果没有传递默认值位1 $id= Input::get('id',1); //获取所有请求内容 Input::all() 取得请求 URI $uri = Request::p ...
- Anaconda下的python如何写入环境变量中
Anaconda是一个非常好的python管理软件,实际使用起来要比直接用python自带的管理工具更好. 若需要将Anaconda下的python.exe添入环境变量中,需要如下设置 如上图所示,需 ...
- SQL盲注
一.首先输入1和-1 查看输入正确和不正确两种情况 二.三种注入POC LOW等级 ... where user_id =$id 输入 真 and 假 = 假 (1)...where u ...
- c语言:矩阵相乘-矩阵相加 新手练习1
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<string.h> voi ...
- JDBC与Java数据库编程基础
一.JDBC 概述 1.什么是JDBC 称为Java数据库连接,它是一种用于数据库访问的应用程序API,由一组用Java语言编写的类和接口组成,有了JDBC就可以用同一的语法对多种关系数据库进行访问, ...
- ECMAScript 5 compatibility shims for legacy JavaScript engines
ECMAScript 5 compatibility shims for legacy JavaScript engines https://github.com/es-shims/es5-shim
- Linux学习-rsyslog.service :记录登录文件的服务
rsyslog.service 的配置文件:/etc/rsyslog.conf 我们现在知道 rsyslogd 可以负责主机产生的各个信息的登录,而这些信息本身是有『严重等级』之分的, 而且, 这些资 ...
- tcp滑动窗口与拥塞控制
TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现.一.滑动窗口协议 所谓滑动窗口协议,自己理解有两点:1. “窗口 ...
- c++继承详解:共有(public)继承,私有继承(private)继承,保护(protected)继承
公有继承(public)继承.私有继承(private).保护继承(protected)是常用的三种继承方式. 1.公有继承(public) 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时 ...