C++ Primer 学习笔记_54_类和数据抽象 --拷贝构造函数、赋值运算符
拷贝控制
--复制构造函数、赋值操作符
引言:
当定义一个新类型时,须要显式或隐式地指定复制、赋值和撤销该类型的对象时会发生什么– 复制构造函数、赋值操作符和析构函数的作用!
复制构造函数:具有单个形參。该形參(经常使用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式的使用复制构造函数。当将该类型的对象传递给函数或者从函数返回该类型的对象时。将隐式使用复制构造函数。
析构函数:作为构造函数的互补。当对象超出作用域或动态分配的对象被删除时。将自己主动应用析构函数。
赋值操作符:与构造函数一样,复制操作符能够通过指定不同类型的右操作数而重载。右操作数为类类型的版本号比較特殊:假设我们没有编写这种版本号。则编译器将为我们合成一个。
【小心地雷】
通常,编译器为我们合成的复制构造函数函数是很精炼的---它们仅仅做必须的工作,但对于类而言。依赖于默认定义有时会导致灾难!
一、复制构造函数
仅仅有单个形參,并且该形參是对本类类型对象的引用(经常使用const修饰),这种构造函数称为复制构造函数。与默认构造函数一样,复制构造函数可由编译器隐式调用。复制构造函数可用于:
1)依据还有一个同类型的对象显式或隐式初始化一个对象。
2)复制一个对象,将它作为实參传给一个函数。
3)从函数返回时复制一个对象。
4)初始化顺序容器中的元素。
5)依据元素初始化式列表初始化数组元素。
1、对象的定义形式
对于类类型。初始化的复制形式和直接形式有所不同:直接初始化直接调用与实參匹配的构造函数。复制初始化式总是调用复制构造函数。[复制初始化首先使用指定构造函数创建一个暂时对象,然后复制构造函数将那个暂时对象复制到正在创建的对象!]
//理解下列语句的差别
string null_book = "9-99999-999-9";
string dots(10,'.');
string empty_copy = string();
string empty_direct;
对于类类型对象。仅仅有指定单个实參或显式创建一个暂时对象用于复制时,才使用复制初始化!
支持初始化的复制形式主要是为了与C的使用方法兼容,当情况许可时。能够同意编译器跳过复制构造函数函数直接创建对象,可是编译器没有义务这样做!
通常直接初始化和复制初始化仅在低级别上存在差异。然而,对于不支持复制的类型,或者使用非explicit构造函数的时候,它们有本质差别:
ifstream file1("filename"); //OK
ifstream file2 = "filename";//Error,由于IO类型的对象不能复制 Sales_item item = string("9-99999-999-9");
2、形參与返回值
当形參为非引用类型的时候,将复制实參的值。相似的,以非引用类型作返回值时,将返回return语句中值的副本。
//当形參/返回值为类类型时,由复制构造函数进行复制。
//然而该函数的形參是const的引用,因此不会复制
string make_plural(size_t,const string &,const string &);
3、初始化容器元素
//首先使用string默认构造函数创建一个暂时值来初始化svec
//然后使用复制构造函数将暂时值拷贝到svec的每一个元素
vector<string> svec(5);
【推荐】
作为一般规则,除非你想使用容器元素的默认初始值,更有效的办法是,分配一个空容器并将已知元素的值增加容器。
4、构造函数与数组元素
假设没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每一个元素。
然而,假设使用常规的花括号括住的数组初始化列表来提供显式元素初始化式,则使用复制初始化来初始化每一个元素。依据指定值创建适当类型的元素,然后用复制构造函数将该值拷贝到相应元素:
Sales_item arrItem[] = {string("0-201-16487-6"),
string("0-201-54848-6"),
string("0-201-82470-6"),
Sales_item()
};
合成的复制构造函数
合成复制构造函数的行为是:运行逐个(非static)成员初始化,将新对象初始化为原对象的副本!
合成复制构造函数直接复制内置类型成员的值,类类型成员使用该类的复制构造函数进行复制。数组成员的复制是个例外。尽管一般不能复制数组,但假设一个类具有数组成员,则合成复制构造函数将复制数组。
复制数组时合成复制构造函数将复制数组的每一个元素。
逐个成员初始化最简单的概念模型是,将合成复制构造函数看作这样一个构造函数:当中每一个数据成员在构造函数初始化列表中进行初始化。
class Sales_item
{
public:
Sales_item(const Sales_item &);
private:
std::string isbn;
int units_sold;
double revenue;
}; Sales_item::Sales_item(const Sales_item &orig):
isbn(orig.isbn),units_sold(orig.units_sold),revenue(orig.revenue) {}
定义自己的复制构造函数
class Foo
{
public:
Foo();
Foo(const Foo &); //复制构造函数
};
复制构造函数的形參一般是一个const引用。由于由于向函数传递对象和从函数返回对象。该构造函数一般不应设置为explicit!
对很多类而言,合成复制构造函数仅仅完毕必要的工作。仅仅包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数,也能够复制。
然而。有些类必须对复制对象时发生的事情加以控制。这种类经常有一个数据成员是指针或者有成员表示在构造函数中分配的其它资源,而还有一些类在创建新对象时必须做一些特定工作。
这两种情况下。都必须定义复制复制构造函数。
通常,定义复制构造函数最困难的部分在于认识到须要复制构造函数O(∩_∩)O~。仅仅要能认识到须要复制构造函数,定义构造函数一般很easy。复制构造函数的定义与其它构造函数一样:它与类同名,没有返回值,能够(并且应该)使用构造函数初始化列表初始化新创建对象的成员,能够在函数体中做不论什么其它必要工作。
//P410 习题13.4
class NoName
{
public:
NoName():pstring(new std::string),i(0),d(0){} NoName(const NoName &temp):i(temp.i),d(temp.d)
{
pstring = new std::string;
*pstring = *(temp.pstring);
} private:
std::string *pstring;
int i;
double d;
};
禁止复制
有些类须要全然禁止复制。
比如,iostream类就不同意复制。
假设想要禁止复制,似乎能够省略复制构造函数,然而,假设不定义复制构造函数,编译器将合成一个。
通过声明但不定义private复制构造函数能够禁止不论什么复制类类型对象的尝试:用于代码中的复制尝试将在编译时标记为错误,而成员函数和友元中的复制尝试将在链接时导致错误。
大多数类应定义复制构造函数和默认构造函数
不定义复制构造函数和/或默认构造函数,会严重局限类的使用:不同意复制的类对象仅仅能作为引用传递给函数或从函数返回,它们也不能用作容器的元素。
一般来说,最好显式或隐式定义默认构造函数和复制构造函数。仅仅有不存在其它构造函数时才合成默认构造函数。
假设定义了复制构造函数,也必须定义默认构造函数。
二、赋值操作符
与复制构造函数一样,假设类未定义自己的赋值操作符,则编译器会合成一个!
1、介绍重载赋值
重载操作符是一些函数,其名字为operator后跟着所定义的操作符的符号。因此,通过定义名为operator=的函数,我们能够对赋值进行定义。像不论什么其它函数一样,操作符函数有一个返回值和一个形參表。形參表必须具有与该操作符数目同样的形參(假设操作符是一个类成员,则包含隐式this形參)。赋值是二元运算,所以该操作符函数有两个形參:第一个形參相应着左操作数,第二个形參相应右操作数。
大多数操作符能够定义为成员函数或非成员函数。
当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。
有些操作符(包含赋值操作符)必须是定义自己的类的成员。
由于赋值必须是类的成员,所以this绑定到指向左操作数的指针。因此,赋值操作符接受单个形參,且该形參是同一类类型的对象。右操作数一般作为const引用传递。
赋值操作符也返回对同一类类型的引用。
class Sales_item
{
public:
Sales_item &operator=(const Sales_item &);
};
2、合成赋值操作符
合成赋值操作符会运行逐个成员赋值:右操作数对象的每一个成员赋值给左操作数对象的相应成员。
除数组之外,每一个成员用所属类型的常规方式进行赋值。对于数组,给每一个数组元素赋值。
如:
Sales_item &Sales_item::operator=(const Sales_item &rhs)
{
isbn = rhs.isbn;
units_sold = rhs.units_sold;
revenue = rhs.revenue; return *this;
}
3、复制和赋值常一起使用
实际上,应该将复制构造函数和赋值操作符看做一个单元,假设须要当中一个,我们差点儿也能够肯定须要还有一个!
//P412 习题13.9
NoName &NoName::operator=(const NoName &rhs)
{
if (pstring)
{
delete pstring;
}
pstring = new string();
*pstring = *(rhs.pstring);
i = rhs.i;
d = rhs.d; return *this;
}
//习题13.10
class Employee
{
public:
typedef unsigned int num_type; Employee(const std::string Name = "NoName"):name(Name),mark(count)
{
set();
}
Employee(const Employee &rhs):name(rhs.name),mark(count)
{
set();
}
~Employee()
{
-- count;
} Employee &operator=(const Employee &rhs)
{
name = rhs.name;
return *this;
} ostream &output(ostream &os)
{
os << "Name: " << name << "\t\tMark: " << mark << endl;
return os;
} private:
std::string name;
num_type mark;
static num_type count; void set()
{
++ count;
}
}; Employee::num_type Employee::count = 0;
版权声明:本文博客原创文章,博客,未经同意,不得转载。
C++ Primer 学习笔记_54_类和数据抽象 --拷贝构造函数、赋值运算符的更多相关文章
- C++ Primer 学习笔记_57_类和数据抽象 --管理指针成员
复印控制 --管理指针成员 引言: 包括指针的类须要特别注意复制控制.原因是复制指针时.一个带指针成员的指针类 class HasPtr { public: HasPtr(int *p,int i): ...
- C++ Primer 学习笔记_53_类和数据抽象 --友元、static员
分类 --友元.static成员 一.友元 友元机制同意一个类将对其.友元关系:一个样例 如果一个窗体管理类Window_Mgr可能须要訪问由其管理的Screen对象的内部数据.Screen应该同意其 ...
- C++ Primer 学习笔记_56_ 类和数据抽象 --消息处理演示示例
拷贝控制 --消息处理演示样例 说明: 有些类为了做一些工作须要对复制进行控制. 为了给出这种样例,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message类和 Folder类分别表示电子 ...
- C++学习笔记(7)----类的数组中构造函数和析构函数的调用顺序
C++类的数组中构造函数和析构函数的调用顺序(2) 对于如下的代码: #include<iostream> using namespace std; class CBase { priva ...
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- Java学习笔记——File类之文件管理和读写操作、下载图片
Java学习笔记——File类之文件管理和读写操作.下载图片 File类的总结: 1.文件和文件夹的创建 2.文件的读取 3.文件的写入 4.文件的复制(字符流.字节流.处理流) 5.以图片地址下载图 ...
- python学习笔记4_类和更抽象
python学习笔记4_类和更抽象 一.对象 class 对象主要有三个特性,继承.封装.多态.python的核心. 1.多态.封装.继承 多态,就算不知道变量所引用的类型,还是可以操作对象,根据类型 ...
- Java学习笔记之---类和对象
Java学习笔记之---类和对象 (一)类 类是一个模板,它描述一类对象的行为和状态 例如:动物类是一个类,动物们都有属性:颜色,动物们都有行为:吃饭 public class Dog { Stri ...
随机推荐
- Hdu-1565 电网接入(1) (国家压缩dp获得冠军
正方形格通路(1) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- html风格的滚动条
DIV辊棒的设置 (CSS)2008/09/26 03:07div 控制滚动条2008年01月06日本 星期日 01:181)隐藏滚动条<body style="overflow-x: ...
- Windows Phone 的控件倾斜效果
原文:Windows Phone 的控件倾斜效果 Windows Phone 7的系统设置里,按钮都有一个点击倾斜的效果,但自己添加的控件就没有.但微软提供了这个效果的代码:TiltEffect MS ...
- 经验19--C#大事
以前学过C#大事.但我还没有搞懂怎么弄. 这一次,他们下进行了研究. 1.定义参数类,对于参数的传递活动.(可以省略) public class UserEventArgs { ...
- 条形码/二维码之开源利器ZXing图文介绍(转)
继前面介绍的一个日本开源软件(该软件只能实现QRCode)原文: Java实现二维码QRCode的编码和解码(http://sjsky.iteye.com/blog/1136934 ),今发现又一优秀 ...
- Android多线程.断点续传下载
多线程,可断点续传的demo!最早写于2010.7! /** * @brief 主界面 * @author lixp */ public class HomeActivity exten ...
- Project_2007关键
本人今天成功用这个密钥,安装project2007. 分享给着急的小伙伴们. W2JJW-4KYDP-2YMKW-FX36H-QYVD8 版权声明:本文博客原创文章.博客,未经同意,不得转载.
- Effective C++:规定27:尽量少做动作的过渡
(一个)C风格遗留转换: (T)expression T(expression) (二)C++提供四种新式转型: (1)const_cast<T>(expression):去除表达式的常量 ...
- 微通道产品经理Grover采访:美国的微通道设计
"'哥'在中国是一种尊称吗?哈哈.我们平时都叫张小龙'龙哥'." "是的.Dan哥,当你认为某个人牛逼的时候,你就能够叫他'哥'." 我对于Dan Grover ...
- 【翻译自mos文章】rman 备份时报:ORA-02396: exceeded maximum idle time
rman 备份时报:ORA-02396: exceeded maximum idle time 參考原文: RMAN backup faling with ORA-02396: exceeded ma ...