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++默认生成并调用的函数的更多相关文章

  1. 读书笔记 effective c++ Item 37 永远不要重新定义继承而来的函数默认参数值

    从一开始就让我们简化这次的讨论.你有两类你能够继承的函数:虚函数和非虚函数.然而,重新定义一个非虚函数总是错误的(Item 36),所以我们可以安全的把这个条款的讨论限定在继承带默认参数值的虚函数上. ...

  2. 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数

    关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...

  3. 读书笔记 effective c++ Item 12 拷贝对象的所有部分

    1.默认构造函数介绍 在设计良好的面向对象系统中,会将对象的内部进行封装,只有两个函数可以拷贝对象:这两个函数分别叫做拷贝构造函数和拷贝赋值运算符.我们把这两个函数统一叫做拷贝函数.从Item5中,我 ...

  4. 读书笔记 effective c++ Item 13 用对象来管理资源

    1.不要手动释放从函数返回的堆资源 假设你正在处理一个模拟Investment的程序库,不同的Investmetn类型从Investment基类继承而来, class Investment { ... ...

  5. 读书笔记 effective c++ Item 14 对资源管理类的拷贝行为要谨慎

    1. 自己实现一个资源管理类 Item 13中介绍了 “资源获取之时也是初始化之时(RAII)”的概念,这个概念被当作资源管理类的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如 ...

  6. 读书笔记 effective c++ Item 15 在资源管理类中提供对原生(raw)资源的访问

    1.为什么需要访问资源管理类中的原生资源  资源管理类是很奇妙的.它们是防止资源泄漏的堡垒,没有资源泄漏发生是设计良好的系统的一个基本特征.在一个完美的世界中,你需要依赖这样的类来同资源进行交互,绝不 ...

  7. 读书笔记 effective c++ Item 17 使用单独语句将new出来的对象放入智能指针

    1. 可能会出现资源泄漏的一种用法 假设我们有一个获取进程优先权的函数,还有一个在动态分类的Widget对象上根据进程优先权进行一些操作的函数: int priority(); void proces ...

  8. 读书笔记 effective c++ Item 24 如果函数的所有参数都需要类型转换,将其声明成非成员函数

    1. 将需要隐式类型转换的函数声明为成员函数会出现问题 使类支持隐式转换是一个坏的想法.当然也有例外的情况,最常见的一个例子就是数值类型.举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐 ...

  9. 读书笔记 effective c++ Item 25 实现一个不抛出异常的swap

    1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11).Sw ...

随机推荐

  1. 11、手把手教你Extjs5(十一)模块界面的总体设计

    上一节中设计了一些模块自定义中用到的要素,为了直观起见,这一节先建立一个模块的主界面.看过我 模块管理常规功能自定义系统的设计与实现 博客的人应该会有所了解了.一个模块的主界面是一个Grid,在其上方 ...

  2. FFT初解(转)

    源:FFT初解 一.前言 首先申明俺不是一个算法工程师,俺是一个底层驱动工程师,有人会发问一个底层驱动工程师需要这个吗?但是我不幸的告诉你,确实是需要的,不过我们不要像算法工程师那样搞得很精通,但是还 ...

  3. STM8的GPIO驱动

    芯片的外设一般按照这么几个流程来进行,GPIO,外部中断,定时器,串口,ADC,IIC,SPI,下面我就按照各个模式来写 首先是GPIO,STM8的GPIO拥有复用功能,这句话告诉我们必然需要配置IO ...

  4. MQ-2烟雾传感器启动

    MQ-2气体传感器所使用的气敏材料是在清洁空气中电导率较低的二氧化锡(SnO2).当传感器所处环境中 存在可燃气体时,传感器的电导率随空气中可燃气体浓度的增加而增大.使用简单的电路即可将电导率的 变化 ...

  5. HIDKomponente使用读写Hid设备(转)

    源:HIDKomponente使用读写Hid设备一瞥 HIDKomponente 是delphi中使用的第三方Hid控件库,可以检测.控制连接到电脑的Hid设备.一般情况下多为usb设备. HIDKo ...

  6. Struts加入拦截器后取不到页面参数

    在Struts2的demo项目中添加了一个简单的拦截器,突然发现,Action中取不到页面的参数了 这也是很蛋疼的事情,还好这个比较简单,稍微一查就发现问题: Struts2中很多的功能是用拦截器实现 ...

  7. onethink的熟悉

    2014.07.14 下载后,并安装成功! 发现一个安装的问题.安装时,无法直接成功. 修改Url 直接跳到最后一步,实现了安装.去官网查询,发现是程序的问题. 尝试构建企业官网. 首先 实现一个企业 ...

  8. DataTabel DataSet 对象 转换成json

    public class DataTableConvertJson    { #region dataTable转换成Json格式        /// <summary>         ...

  9. RACSingle 有效的两种方式

    第一种当然是subscribeNext 另外还有一种就是作为Command的enablesingle也相当于被订阅了.

  10. window 2008+apache2.4.4+php5.5+mysql-5.6.12+phpmyadmin4.0.4.1安装过程(参考他人文章基础上加上自己遇到的问题)

    一.window server2008的安装 1.我用U盘安装的,先用UltraISO把server2008刻录到U盘中,过程我搜了一下,帖个地址: http://wenku.baidu.com/vi ...