读书笔记 effective c++ Item 5 了解c++默认生成并调用的函数
1 编译器会默认生成哪些函数
什么时候空类不再是一个空类?答案是用c++处理的空类。如果你自己不声明,编译器会为你声明它们自己版本的拷贝构造函数,拷贝赋值运算符和析构函数,如果你一个构造函数都没有声明,编译器同样会为你声明一个默认拷贝构造函数。这些所有的函数会是public和inline的(Item30)。因此,如果你写了下面的类:
1 class Empty{};
本质上来说和下面的类是一样的:
1 class Empty { 2 3 public: 4 5 Empty() { ... } // default constructor 6 7 Empty(const Empty& rhs) { ... } // copy constructor 8 9 ~Empty() { ... } // destructor — see below 10 11 // for whether it’s virtual 12 13 Empty& operator=(const Empty& rhs) { ... } // copy assignment operator 14 15 };
这些函数只有在被需要的时候才会生成。程序中需要这些函数是很平常的事。下面的代码会导致相应的函数被生成:
1 Empty e1; // default constructor; destructor 2 3 Empty e2(e1); // copy constructor 4 5 e2 = e1; // copy assignment operator
2 默认生成的函数会做些什么?
考虑到编译器会为你生成函数,这些函数会做些什么?默认拷贝构造函数和析构函数会给编译器腾出一个地方用来放“藏在幕后的代码”,像基类和非静态数据成员的构造函数和析构函数的调用。注意生成的默认析构函数不是虚函数(Item7),除非生成默认析构函数的类继承自一个声明了虚析构函数的基类(这样默认析构函数的虚或者非虚继承自基类)。
2.1 默认拷贝构造函数
对于拷贝构造函数和拷贝赋值运算符来说,编译器生成的版本只是将源对象的非静态数据成员简单的拷贝到目标对象上。例如,考虑一个NamedObject 模板类允许你同类型T的对象进行关联。
1 template<typename T> 2 3 class NamedObject { 4 5 public: 6 7 NamedObject(const char *name, const T& value); 8 9 NamedObject(const std::string& name, const T& value); 10 11 ... 12 13 private: 14 15 std::string nameValue; 16 17 T objectValue; 18 19 };
因为在NamedObject中声明了一个构造函数,编译器不再为其生成默认构造函数。这很重要,如果你仔细的设计了一个需要带参构造函数的类,你不必担心编译器会添加一个不带参数的构造函数来覆盖你的版本。
NamedObject既没有声明拷贝构造函数也没有声明拷贝赋值运算符,所以编译器会产生这些函数(如果它们被需要)。看下面的例子,拷贝构造函数这么使用:
1 NamedObject<int> no1("Smallest Prime Number", 2); 2 3 NamedObject<int> no2(no1); // calls copy constructor
编译器生成的拷贝构造函数必须分别用no1.nameValue和no1.objectValue初始化no2.nameValue和no2.objectValue。nameValue的类型是string,标准的string类型有个拷贝构造函数,于是no2.nameValue会通过调用string的拷贝构造函数来进行初始化,构造函数用no1.nameValue作为参数。另外,NamedObject<int>::objectValue的类型是int,于是no2.objectValue会通过拷贝no1.objectValue的bits来进行初始化。
2.2 默认拷贝赋值运算符
编译器为NamedObject<int>生成的拷贝赋值运算符的行为同拷贝构造函数基本上是一样的。但是一般来说,编译器生成的拷贝赋值运算符只有在生成的代码合法,并且有合理的机会证明其有意义,行为同拷贝构造函数才是一样的。如果不满上述任何一个条件,编译器都会拒绝为你的类生成operator=。
举个例子:考虑NamedObject像下面这样定义,nameValue是指向string的引用,objectValue是const T.
1 template<typename T> 2 3 class NamedObject { 4 5 public: 6 7 // this ctor no longer takes a const name, because nameValue 8 9 // is now a reference-to-non-const string. The char* constructor 10 11 // is gone, because we must have a string to refer to. 12 13 NamedObject(std::string& name, const T& value); 14 15 ... // as above, assume no 16 17 // operator= is declared 18 19 private: 20 21 std::string& nameValue; // this is now a reference 22 23 const T objectValue; // this is now const 24 25 };
考虑下面会发生什么:
1 std::string newDog("Persephone"); 2 3 std::string oldDog("Satch"); 4 5 NamedObject<int> p(newDog, 2); // when I originally wrote this, our 6 7 // dog Persephone was about to 8 9 // have her second birthday 10 11 NamedObject<int> s(oldDog, 36); // the family dog Satch (from my 12 13 // childhood) would be 36 if she 14 15 // were still alive 16 17 p = s; // what should happen to the data members in p?
在赋值之前,p.nameValue和s.nameValue都会指向string对象,虽然不是同一个。赋值如何影响p.nameValue?在赋值之后,p.nameValue应该指向s.nameValue所指向的那个string么?也就是,引用本身会改变么?如果是这样,就开辟了一块新天地,因为c++没有提供使引用指向另一个对象的方法。或者,p.nameValue指向的string对象应该被修改?这样就会影响其他对象,这些对象持有指向这个被修改的string的指针或者引用。这是编译器生成的拷贝赋值运算符应该做得?
面对这个难题,c++拒绝编译代码。如果你想在一个类中支持含有引用成员的拷贝赋值运算符,你必须自己定义一个拷贝赋值运算符。对于包含const成员的类,编译器的行为也是类似的。修改const成员是不合法的,因此当一个默认拷贝赋值运算符生成时,编译器对如何处理它们是不确定的。最后,如果在基类中将拷贝赋值运算符声明成private,编译器拒绝在派生类中生成拷贝赋值运算符。毕竟编译器为派生类生成的拷贝赋值运算符需要能够处理基类的部分,在这种情况下,它们当然不能够触发派生类没有权限调用的函数。
读书笔记 effective c++ Item 5 了解c++默认生成并调用的函数的更多相关文章
- 读书笔记 effective c++ Item 37 永远不要重新定义继承而来的函数默认参数值
从一开始就让我们简化这次的讨论.你有两类你能够继承的函数:虚函数和非虚函数.然而,重新定义一个非虚函数总是错误的(Item 36),所以我们可以安全的把这个条款的讨论限定在继承带默认参数值的虚函数上. ...
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...
- 读书笔记 effective c++ Item 12 拷贝对象的所有部分
1.默认构造函数介绍 在设计良好的面向对象系统中,会将对象的内部进行封装,只有两个函数可以拷贝对象:这两个函数分别叫做拷贝构造函数和拷贝赋值运算符.我们把这两个函数统一叫做拷贝函数.从Item5中,我 ...
- 读书笔记 effective c++ Item 13 用对象来管理资源
1.不要手动释放从函数返回的堆资源 假设你正在处理一个模拟Investment的程序库,不同的Investmetn类型从Investment基类继承而来, class Investment { ... ...
- 读书笔记 effective c++ Item 14 对资源管理类的拷贝行为要谨慎
1. 自己实现一个资源管理类 Item 13中介绍了 “资源获取之时也是初始化之时(RAII)”的概念,这个概念被当作资源管理类的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如 ...
- 读书笔记 effective c++ Item 15 在资源管理类中提供对原生(raw)资源的访问
1.为什么需要访问资源管理类中的原生资源 资源管理类是很奇妙的.它们是防止资源泄漏的堡垒,没有资源泄漏发生是设计良好的系统的一个基本特征.在一个完美的世界中,你需要依赖这样的类来同资源进行交互,绝不 ...
- 读书笔记 effective c++ Item 17 使用单独语句将new出来的对象放入智能指针
1. 可能会出现资源泄漏的一种用法 假设我们有一个获取进程优先权的函数,还有一个在动态分类的Widget对象上根据进程优先权进行一些操作的函数: int priority(); void proces ...
- 读书笔记 effective c++ Item 24 如果函数的所有参数都需要类型转换,将其声明成非成员函数
1. 将需要隐式类型转换的函数声明为成员函数会出现问题 使类支持隐式转换是一个坏的想法.当然也有例外的情况,最常见的一个例子就是数值类型.举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐 ...
- 读书笔记 effective c++ Item 25 实现一个不抛出异常的swap
1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11).Sw ...
随机推荐
- HTML学习(五)链接
1.创建文本链接 <html> <body> <p> <a href="/index.html">本文本</a> 是一个 ...
- JNI 中文字符串传递(转)
源:JNI 中文字符串传递 因为项目编码中通过JNI传递中文字符时出现乱码问题,特搜集了相关资料,整理如下: java内部是使用16bit的unicode编码(UTF-16)来表示字符串的,无论中文英 ...
- php传输大数据大文件时候php.ini相关设置
post_max_size which is directly related to the POST size---针对采用post上传的,大文件,此项为关键 upload_max_filesize ...
- 浅谈MySQL分表
关于分表:顾名思义就是一张数据量很大的表拆分成几个表分别进行存储. 我们先来大概了解以下一个数据库执行SQL的过程: 接收到SQL --> 放入SQL执行队列 --> 使用分析器分解SQL ...
- javascript中的字典
1.概念 字典是一种以键值对的形式存储的数据结构,就系那个电话本中的名字和电话号码一样.要找到一个电话首先要找到名字,再根据名字找到电话号码.这里的键就是指用来查找的东西,值就是查找得到的结果. Ja ...
- ui主线程控件的更新就让这个activity的异步任务做完整
项目中使用的SingleMessageView,控件实例化后,点击用户头像,此时跳转到UserInfo里查看这个用户的头像.用户名.签名.标签. 之前,师兄在SingleMessage里写了个头像的点 ...
- [Angular Tutorial] 6-Two-way Data Binding
在这一步中,您将会添加一个新特性来使得您的用户可以控制电话列表中电话的顺序,动态改变顺序是由创建一个新的数据模型的特性实现的,将它和迭代器绑定在一起,并且让数据绑定神奇地处理下面的工作. ·除了搜索框 ...
- 环信 之 iOS 客户端集成三:基础功能
SDK中,大部分与网络有关的操作,都有三种方法: 同步方法 通过delegate回调的异步方法.要想能收到回调,必须要注册为:[[EaseMob sharedInstance].chatManager ...
- OC 优化目录
把 main. info 和 appdelegate 放到自己的新建目录 1.去掉info.plist的警告 在build phases->copy Bundle Resources中去掉inf ...
- python 自动化之路 day 14
今日内容 http://www.cnblogs.com/wupeiqi/articles/5699254.html 群共享 s15Html课件 1. paramiko模块 https://github ...