如果你没有声明下列函数,体贴的编译器会声明它自己的版本。这些函数是:一个拷贝构造函数,一个赋值运算符,一个析构函数,一对取址运算符。另外,如果你没有声明任何构造函数,它也将为你声明一个缺省构造函数。所有这些函数都是公有的。换句话说,如果你这么写:

class Empty{};

和你这么写是一样的:

class Empty {
public:
Empty(); // 缺省构造函数
Empty(const Empty& rhs); // 拷贝构造函数 ~Empty(); // 析构函数 ---- 是否
// 为虚函数看下文说明
Empty&
operator=(const Empty& rhs); // 赋值运算符 Empty* operator&(); // 取址运算符
const Empty* operator&() const;
};

假设编译器为你写了函数,这些函数又做些什么呢?是这样的,缺省构造函数和析构函数实际上什么也不做,它们只是让你能够创建和销毁类的对象。注意,生成的析构函数一般是非虚拟的(参见条款14),除非它所在的类是从一个声明了虚析构函数的基类继承而来。缺省取址运算符只是返回对象的地址。这些函数实际上就如同下面所定义的那样:

inline Empty::Empty() {}

inline Empty::~Empty() {}

inline Empty * Empty::operator&() { return this; }

inline const Empty * Empty::operator&() const
{ return this; }

至于拷贝构造函数和赋值运算符,官方的规则是:缺省拷贝构造函数(赋值运算符)对类的非静态数据成员进行 "以成员为单位的" 逐一拷贝构造(赋值)。即,如果m是类C中类型为T的非静态数据成员,并且C没有声明拷贝构造函数(赋值运算符),m将会通过类型T的拷贝构造函数(赋值运算符)被拷贝构造(赋值)---- 如果T有拷贝构造函数(赋值运算符)的话。如果没有,规则递归应用到m的数据成员,直至找到一个拷贝构造函数(赋值运算符)或固定类型(例如,int,double,指针,等)为止。默认情况下,固定类型的对象拷贝构造(赋值)时是从源对象到目标对象的 "逐位" 拷贝。对于从别的类继承而来的类来说,这条规则适用于继承层次结构中的每一层,所以,用户自定义的构造函数和赋值运算符无论在哪一层被声明,都会被调用。

编译器生成的赋值运算符要想正常工作,与此相关的所有代码必须合法且行为上要合理。如果这两个条件中有一个不成立,编译器将拒绝为你的类生成operator=

class NamedObject
{
public:
NamedObject(string &name, const int &value)
:nameValue(name), objectValue(value){}
private:
string &nameValue;
const int objectValue;
}; void main()
{
string newDog("persephone");
string oldDog("satch"); NamedObject p(newDog, );
NamedObject s(oldDog, ); p = s;//"operator =”函数在“NamedObject”中不可用
}

赋值之前,p.nameValue指向某个string对象,s.nameValue也指向一个string,但并非同一个。赋值会给p.nameValue带来怎样的影响呢?赋值之后,p.nameValue应该指向 "被s.nameValue所指向的string" 吗,即,引用本身应该被修改吗?如果是这样,那太阳从西边出来了,因为C++没有办法让一个引用指向另一个不同的对象(参见条款M1)(这点其实不用考虑,因为肯定是赋值而不是改变引用指向的对象,但是因为以下的原因,所以不能赋值,若赋值,其他指向那个string的指针或引用也会受到影响)。或者,p.nameValue所指的string对象应该被修改吗? 这样的话,含有 "指向那个string的指针或引用" 的其它对象也会受影响,也就是说,和赋值没有直接关系的其它对象也会受影响。这是编译器生成的赋值运算符应该做的吗?

面对这样的难题,C++拒绝编译这段代码。如果想让一个包含引用成员的类支持赋值,你就得自己定义赋值运算符。对于包含const成员的类(例如上面被修改的类中的objectValue)来说,编译器的处理也相似;因为修改const成员是不合法的,所以编译器在隐式生成赋值函数时也会不知道怎么办。还有,如果派生类的基类将标准赋值运算符声明为private,  编译器也将拒绝为这个派生类生成赋值运算符。因为,编译器为派生类生成的赋值运算符也应该处理基类部分(见条款16和M33),但这样做的话,派生类对象就得调用对派生类来说无权访问的基类成员函数,这当然是不可能的。


指针与引用的区别

1.在任何情况下都不能使用指向空值的引用,一个引用必须总是指向某些对象,所以引用定义时要被初始化。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量

2.指针可以被重新赋值以指向另一个不同的对象,但是引用总是指向在初始化时被指定的对象,以后不能改变

string s1("nancy");
string s2("clancy"); string &rs = s1; rs = s2;//改变了s1及rs的值,但是rs仍然指向s1

3.当重载某个操作符时,应该使用引用。

vector<int>v(10);

v[5]=10;

如果操作符[]返回一个指针,那么后一个语句得这样写:

*v[5]=10;

总结:当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针。

条款45: 弄清C++在幕后为你所写、所调用的函数的更多相关文章

  1. Effective C++ -----条款09:绝不在构造和析构过程中调用virtual函数

    在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层).

  2. 条款9:不要在构造和析构过程中调用virtual函数

    如下是一个股票交易的例子: class Transaction // 交易的基类 { public: Transaction(); ; // 用于记录交易日志 }; Transaction::Tran ...

  3. 条款9:绝不在构造和析构过程中调用virtual函数(Never call virtual functions during construction or destruction)

    NOTE:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)

  4. 条款09:绝不在构造和析构过程中调用virtual函数

    不该在构造函数和析构函数期间调用virtual函数,这一点是C++与jave/C#不同的地方之一. 假设有一个class继承体系,用来模拟股市交易如买进.卖出的订单等等.这样的交易一定要经过审计,所以 ...

  5. 读书笔记_Effective_C++_条款三十六:绝不重新定义继承而来的non-virtual函数

    这个条款的内容很简单,见下面的示例: class BaseClass { public: void NonVirtualFunction() { cout << "BaseCla ...

  6. Effective C++ 条款45

    本节条款的题目是运用成员模板接受全部兼容类型 作者阐述自己的观点是通过智能指针的样例. 在学习本节条款之前我们要先明确关于隐式转化的问题 例如以下代码: #include<iostream> ...

  7. Effective C++ -----条款45:运用成员函数模板接受所有兼容类型

    请使用member function templates(成员函数模板)生成”可接受所有兼容类型“的函数. 如果你声明member templates 用于“泛化copy构造”或“泛化assignme ...

  8. 条款05:了解C++默默编写并调用哪些函数

    每一个class都会有一个或多个构造函数.一个析构函数.一个copy assignment操作符.这些控制着基础操作,像是产出新对象并确保它被初始化.摆脱旧对象并确保它被适当清理.以及赋予对象新值. ...

  9. Effective C++_笔记_条款05_了解C++默认编写并调用哪些函数

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 直接上代码: 1: class Empty{}; 如果你写了这样 ...

随机推荐

  1. int 多少位,long 多少位

    Int16 值类型表示值介于 -32768 到 +32767 之间的有符号整数. Int32 值类型表示值介于 -2,147,483,648 到 +2,147,483,647 之间的有符号整数. In ...

  2. Vue组件之间通信的三种方式

    最近在看梁颠编著的<Vue.js实战>一书,感觉颇有收获,特此记录一些比价实用的技巧. 组件是MVVM框架的核心设计思想,将各功能点组件化更利于我们在项目中复用,这类似于我们服务端面向对象 ...

  3. java 利用Xstream注解生成和解析xml

    https://www.oschina.net/code/snippet_116183_14202#23325

  4. 配置Tomcat && Http简介

    WEB环境搭建 1. Web服务器 Web服务器主要用来接收客户端发送的请求和响应客户端请求. 作为JavaWeb程序来说,还需要有Servet容器,容器的主要作用就是调用java程序处理用户发送的请 ...

  5. MySQL详解(25)-----------MySQL性能优化

    1.    简介 在Web应用程序体系架构中,数据持久层(通常是一个关系数据库)是关键的核心部分,它对系统的性能有非常重要的影响.MySQL是目前使用最多的开源数据库,但是MySQL数据库的默认设置性 ...

  6. Fiddler——抓包工具的使用

    fiddler安装 pc端安装fiddler,自行从百度下载即可 Fiddler是强大且好用的Web调试工具之一,它能记录客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输 ...

  7. CherryPy 入门

    CherryPy是一个Python的HTTP框架,可以用Python来处理HTTP请求然后返回结果. 1. 安装 可以去这个地址下载 CherryPy-3.1.2.win32.exe .或者去这个链接 ...

  8. 还是关于编码——decode & encode的探究

    最近被py3.4中的编码折磨的不要不要的,decode & encode的使用.功能貌似在2.7—3.0有一个巨大的变化.网上查询的一些解答很多是基于2.7中的unicode功能,给出的解答是 ...

  9. Computed Properties vs Property Requirements - protocol

    In addition to stored properties, classes, structures, and enumerations can define computed properti ...

  10. Discuz伪静态代码

    <?php /** * [伪静态跳转(xugui_redirect.{modulename})] (C)2012-2099 Powered by 懒人V难人. * Version: 1.0 * ...