<Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<一>
<Item 5> Know what functions C++ silently writes and calls
1、If you don't declare them yourself, compilers will declare their own versions of a copy constructor, a copy assignment operator, and a destructor. Furthermore, if you declare no constructors at all, compilers will also declare a default constructor for you. All these functions will be both public and inline (see Item 30).These functions are generated only if they are needed, but it doesn't take much to need them. The following code will cause each function to be generated:
Empty e1; // default constructor;
// destructor
Empty e2(e1); // copy constructor
e2 = e1; // copy assignment operator
2、编译器默认生成的函数都回做些什么呢?Well, the default constructor and the destructor primarily give compilers a place to put "behind the scenes" code such as invocation of constructors and destructors of base classes and non-static data members.Note that the generated destructor is non-virtual (see Item7) unless it's for a class inheriting from a base class that itself declares a virtual destructor (in which case the function's virtualness comes from the base class). As for the copy constructor and the copy assignment operator, the compiler-generated versions simply copy each non-static data member of the source object over to the target object.
3、compiler-generated copy assignment operators behave as I've described only when the resulting code is both legal and has a reasonable chance of making sense. If either of these tests fails, compilers will refuse to generate an operator= for your class.
template<class T>
class NamedObject {
public:
// this ctor no longer takes a const name, because nameValue
// is now a reference-to-non-const string. The char* constructor
// is gone, because we must have a string to refer to.
NamedObject(std::string& name, const T& value);
... // as above, assume no
// operator= is declared
private:
std::string& nameValue; // this is now a reference
const T objectValue; // this is now const
};
std::string newDog("Persephone");
std::string oldDog("Satch");
NamedObject<int> p(newDog, ); // when I originally wrote this, our
// dog Persephone was about to
// have her second birthday
NamedObject<int> s(oldDog, ); // the family dog Satch (from my
// childhood) would be 36 if she
// were still alive
p = s; // what should happen to
// the data members in p?
C++ doesn't provide a way to make a reference refer to a different object.Faced with this conundrum, C++ refuses to compile the code. If you want to support assignment in a class containing a reference member, you must define the copy assignment operator yourself. Compilers behave similarly for classes containing const members. Finally, compilers reject implicit copy assignment operators in derived classes that inherit from base classes declaring the copy assignment operator private.(需要copy基类部分,但是无权访问基类copy函数,因此不会生成)
4、Things to Remember
Compilers may implicitly generate a class's default constructor, copy constructor, copy assignment operator, and destructor.
<Item 6> Explicitly disallow the use of compiler-generated functions you do not want
5、Instead, declare the copy constructor and the copy assignment operator private. By declaring a member function explicitly, you prevent compilers from generating their own version, and by making the function private, you keep people from calling it.
6、如果进一步不想让友元函数和成员函数调用:This trick — declaring member functions private and deliberately not implementing them — is so well established, it's used to prevent copying in several classes in C++'s iostreams library. Take a look, for example, at the definitions of ios_base, basic_ios, and sentry in your standard library implementation. You'll find that in each case, both the copy constructor and the copy assignment operator are declared private and are not defined.(common convention:声明时只使用参数类型省略参数名字),这种类型是link-time error。
7、It's possible to move the link-time error up to compile time (always a good thing — earlier error detection is better than later) by declaring the copy constructor and copy assignment operator private not in HomeForSale itself, but in a base class specifically designed to prevent copying. The base class is simplicity itself:
class Uncopyable {
protected: // allow construction
Uncopyable() {} // and destruction of
~Uncopyable() {} // derived objects...
private:
Uncopyable(const Uncopyable&); //...but prevent copying
Uncopyable& operator=(const Uncopyable&);
}; class HomeForSale: private Uncopyable { // class no longer
... // declares copy ctor or
}; // copy assign. operator
这样HomeForSale就不再能复制和赋值
8、std::vector实现的前提是其中的对象可以复制,大多数情况下,我们应该在其中存放对象的引用而不是值。
9、The implementation and use of Uncopyable include some subtleties, such as the fact that inheritance from Uncopyable needn't be public (see Items 32 and 39) and that Uncopyable 's destructor need not be virtual (see Item 7). Because Uncopyable contains no data, it's eligible for the empty base class optimization described in Item 39, but because it's a base class, use of this technique could lead to multiple inheritance (see Item 40). Multiple inheritance, in turn, can sometimes disable the empty base class optimization (again, see Item 39). In general, you can ignore these subtleties and just use Uncopyable as shown, because it works precisely as advertised. You can also use the version available at Boost (see Item 55). That class is named noncopyable. It's a fine class, I just find the name a bit un-, er, non natural.
10、Things to Remember
To disallow functionality automatically provided by compilers, declare the corresponding member functions private and give no implementations. Using a base class like Uncopyable is one way to do this.
<Item 7> Declare destructors virtual in polymorphic base classes
11、C++ specifies that when a derived class object is deleted through a pointer to a base class with a non-virtual destructor, results are undefined. What typically happens at runtime is that the derived part of the object is never destroyed.Eliminating the problem is simple: give the base class a virtual destructor. Then deleting a derived class object will do exactly what you want. It will destroy the entire object, including all its derived class parts:(即使使用多台,在基类中定义虚析构函数也不一定是真理,例如由于ABI/(Application Binary Interface 应用二进制接口)的缘故,COM就不再接口定义中使用虚析构函数,也不使用new/delete)。Any class with virtual functions should almost certainly have a virtual destructor.
12、If a class does not contain virtual functions, that often indicates it is not meant to be used as a base class. When a class is not intended to be a base class, making the destructor virtual is usually a bad idea.
class Point { // a 2D point public:
Point(int xCoord, int yCoord);
~Point();
private:
int x, y; };
The implementation of virtual functions requires that objects carry information that can be used at runtime to determine which virtual functions should be invoked on the object. This information typically takes the form of a pointer called a vptr ("virtual table pointer"). The vptr points to an array of function pointers called a vtbl ("virtual table"); each class with virtual functions has an associated vtbl. When a virtual function is invoked on an object, the actual function called is determined by following the object's vptr to a vtbl and then looking up the appropriate function pointer in the vtbl.
What is important is that if the Point class contains a virtual function, objects of that type will increase in size. On a 32-bit architecture, they'll go from 64 bits (for the two ints) to 96 bits (for the ints plus the vptr); on a 64-bit architecture, they may go from 64 to 128 bits, because pointers on such architectures are 64 bits in size.
也就是说C++的虚函数,会导致类的内存布局有额外的vptr增长,进而导致程序的ABI变差
13、The bottom line is that gratuitously declaring all destructors virtual is just as wrong as never declaring them virtual. In fact, many people summarize the situation this way: declare a virtual destructor in a class if and only if that class contains at least one virtual function.
It is possible to get bitten by the non-virtual destructor problem even in the complete absence of virtual functions. For example, the standard string type contains no virtual functions, but misguided programmers sometimes use it as a base class anyway:使用非虚析构函数的基类指针释放子类的实例是undefined behavior。
class SpecialString: public std::string { // bad idea! std::string has a ... // non-virtual destructor };
The same analysis applies to any class lacking a virtual destructor, including all the STL container types (e.g., vector, list, set, tr1::unordered_map (see Item 54), etc.). If you're ever tempted to inherit from a standard container or any other class with a non-virtual destructor, resist the temptation! (Unfortunately, C++ offers no derivation-prevention mechanism akin to Java's final classes or C#'s sealed classes.)
14、declare a pure virtual destructor in the class you want to be abstract. Here's an example:
class AWOV { // AWOV = "Abstract w/o Virtuals" public: virtual ~AWOV() = ; // declare pure virtual destructor };
There is one twist, however: you must provide a definition for the pure virtual destructor:
AWOV::~AWOV() {} // definition of pure virtual dtor
The way destructors work is that the most derived class's destructor is called first, then the destructor of each base class is called. Compilers will generate a call to ~AWOV from its derived classes' destructors, so you have to be sure to provide a body for the function. If you don't, the linker will complain.
15、Not all base classes are designed to be used polymorphically. Neither the standard string type, for example, nor the STL container types are designed to be base classes at all, much less polymorphic ones. Some classes are designed to be used as base classes, yet are not designed to be used polymorphically. Such classes — examples include Uncopyable from Item 6 and input_iterator_tag from the standard library (see Item 47) — are not designed to allow the manipulation of derived class objects via base class interfaces. As a result, they don't need virtual destructors.
16、Things to Remember
Polymorphic base classes should declare virtual destructors. If a class has any virtual functions, it should have a virtual destructor.
Classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.
<Item 8>Prevent exceptions from leaving destructors
17、C++ doesn't prohibit destructors from emitting exceptions, but it certainly discourages the practice.容器或者数组局部变量需要析构其中的每个对象,如果两个对象抛出异常,程序同时存在两个异常时是undefined behavior,Premature program termination or undefined behavior can result from destructors emitting exceptions even without using containers and arrays. C++ does not like destructors that emit exceptions!
18、有两种基本方式处理上面的trouble
Terminate the program if close throws, typically by calling abort
DBConn::~DBConn()
{
try { db.close(); }
catch (...) {
make log entry that the call to close failed;
std::abort();
}
}
This is a reasonable option if the program cannot continue to run after an error is encountered during destruction. It has the advantage that if allowing the exception to propagate from the destructor would lead to undefined behavior, this prevents that from happening. That is, calling abort may forestall undefined behavior.
Swallow the exception arising from the call to close :
DBConn::~DBConn()
{
try { db.close(); }
catch (...) {
make log entry that the call to close failed;
}
}
In general, swallowing exceptions is a bad idea, because it suppresses important information — something failed! Sometimes, however, swallowing exceptions is preferable to running the risk of premature program termination or undefined behavior. For this to be a viable option, the program must be able to reliably continue execution even after an error has been encountered and ignored.
19、A better strategy is to design DBConn's interface so that its clients have an opportunity to react to problems that may arise.即给用户手动处理异常的自由 只有用户没有手动处理的时候才在析构函数中调用
class DBConn {
public:
...
void close() // new function for
{ // client use
db.close();
closed = true;
} ~DBConn()
{
if (!closed) {
try { // close the connection
db.close(); // if the client didn't
}
catch (...) { // if closing fails,
make log entry that call to close failed; // note that and
... // terminate or swallow
}
}
private:
DBConnection db;
bool closed;
};
20、Things to Remember
Destructors should never emit exceptions. If functions called in a destructor may throw, the destructor should catch any exceptions, then swallow them or terminate the program.
If class clients need to be able to react to exceptions thrown during an operation, the class should provide a regular (i.e., non-destructor) function that performs the operation.
<Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<一>的更多相关文章
- <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<二>
<Item 9> Never call virtual functions during construction or destruction 1.you shouldn't call ...
- <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不会为访问一个数据成员是否需要使用括号进行函数调 ...
随机推荐
- 树莓派3B+学习笔记:2、更改显示分辨率
1.打开终端,输入 sudo raspi-config 选择第7行: 2.选择第5行: 3.选择一个自己习惯的分辨率(我选择1024X768),确定后重启,VNC会自动连接: 4.更改分辨率完成,方便 ...
- 『Python基础-3』变量、定义变量、变量类型、关键字Python基础-3』变量、定义变量、变量类型、关键字
『Python基础-3』变量.定义变量.变量类型.关键字 目录: 1.Python变量.变量的命名 2.变量的类型(Python数据类型) 3.Python关键字 1. Python 变量.变量的命名 ...
- golang 兼容不同json结构体解析实践
线上服务器,同一个web接口有时需要兼容不同版本的结构体.这种情况思路是使用interface{}接收任意类型数据,结合reflect包处理. 如下,http接口调用者会传入不同的json结构数据(单 ...
- 棋盘覆盖(我们学校自己的UOJ上的变形题)
题目 #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> # ...
- leetcode add_binary 采坑记
尽管add_binary在leetcode中是一个简单难度,但是踩了不少坑,记录一下 描述: 给两个字符串形式的二进制数,要求求和并输出字符串形式的结果,其中a和b均不为空字符串 样例: a=“101 ...
- SpaceVim 语言模块 erlang
原文连接: https://spacevim.org/cn/layers/lang/erlang/ 模块简介 功能特性 启用模块 快捷键 语言专属快捷键 交互式编程 模块简介 这一模块为 SpaceV ...
- P1060 开心的金明
P1060 开心的金明 题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要 ...
- 用PowerDesign反向生成数据库Sql语句问题
在用Pd15反向生成数据库时,生成的Sql语句在Sql Server Manager Studio里面报错,根本就执行不了.数据库用的是Sql Server 2008 R2.经过一番修 ...
- hdu1847Good Luck in CET-4 Everybody!(sg函数)
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- 如何往eclipse中导入maven项目
现在公司中大部分项目可能都是使用maven来构建,假如现在摆在你面前有一个maven的项目,如果你要学习它,如何将它导入到像eclipse这样的集成开发工具中呢,以项目public_class_1为例 ...