C++Primer 第13章复制控制课后习题答案文档格式.docx
《C++Primer 第13章复制控制课后习题答案文档格式.docx》由会员分享,可在线阅读,更多相关《C++Primer 第13章复制控制课后习题答案文档格式.docx(22页珍藏版)》请在冰点文库上搜索。
:
string),i(0),d(0){}
private:
std:
string*pstring;
inti;
doubled;
};
NoName(constNoName&
orig):
pstring(newstring(*(orig.pstring))),i(orig.i),d(orig.d)
{
//pstring=newstring;
//*pstring=*(orig.pstring);
5.哪个类定义可能需要一个复制构造函数?
(a)包含四个float成员的Point3w类。
(b)Matrix类,其中,实际矩阵在构造函数中动态分配,在析构函数中删除。
(c)Payroll类,在这个类中为每个对象提供唯一ID。
(d)Word类,包含一个string和一个以行列位置对为元素的vector。
(b)需要,涉及到指针及动态分配
(c)需要,在根据已存在的Payroll对象创建其副本时,需要提供唯一的ID.
其他的均可以调用编译器的提供的复制构造函数,或者调用类类型的string和vector的复制构造函数。
6.复制构造函数的形参并不限制为const,但必须是一个引用,解释这个限制的基本原理,例如,解释为什么下面的定义不能工作,
Sales_item:
Sales_item(constSales_itemrhs);
它不能工作的原因是:
当形参为非引用类型时,将复制实参的值,给这个copyconstructor,但是,每当以传值方式传递参数时,会导致调用复制构造函数,因此,如果要使用以传值方式传递参数的copyconstructor,必须使用一个“不以传值方式传递参数”的copyconstructor,否则就会导致copyconstructor的无穷递归调用。
这个“不以传值方式传递参数”的方法就是使用形参是一个引用的copyconstructor,即以传地址的方式传递参数。
7.类何时需要定义复制操作符?
在需要定义复制构造函数时,也需要定义赋值操作符,即如果一个类
(1)类中包含指针型数据成员,
(2)或者在进行赋值操作时需要做一些特定工作,则该类需要定义赋值操作符。
8.对于习题13.5中列出的每个类型,指出类是否需要赋值操作符。
(b)需要赋值操作符,因为涉及指针和动态分配内存;
(c)需要,在用已存在的Payroll对象给另一个Payroll对象赋值时,需要提供唯一的ID。
9.习题13.4中包括NoName类的简单定义,确定这个类是否需要赋值操作符,如果需要,实现它。
因为有指针类的数据成员,所以需要赋值操作符,实现为:
NoName&
operator=(constNoName&
Nn)
i(Nn.i);
d(Nn.d);
pstring=newstring;
*pstring=*(Nn.pstring);
return*this;
10.定义一个Employee类,包含雇员名字和一个唯一的雇员标识,为该类定义默认构造函数和参数为表示雇员名字的string构造函数。
如果该类需要复制构造函数或赋值操作符,实现这些函数。
classEmployee
public:
Employee():
ID(cnt){cnt++;
}//默认构造函数
Employee(conststd:
string&
na):
name(na),ID(cnt)
{cnt++;
}//构造函数
//拷贝构造函数
Employee(constEmployee&
rhs):
name(rhs.name),ID(cnt)
{cnt++;
}
//赋值操作符
Employee&
operator=(constEmployee&
rhs)
{
name=rhs.name;
return*this;
stringname;
intID;
staticintcnt;
另外需要在类外对static成员进行初始化:
intEmployee:
cnt=1;
11.什么是析构函数?
合成析构函数有什么用?
什么时候会合成析构函数?
什么时候一个类必须定义自己的析构函数?
析构函数是一个成员函数,它的名字与类的名字相同,在名字前加一个代字符~,没有返回值,没有形参,用于类的对象超出作用域时释放对象所获取的资源,或删除指向动态分配对象的指针。
合成析构函数的作用:
1,按对象创建时的逆序撤销每个非static成员,2,对于类类型的成员,合成析构函数调用该成员的析构函数来撤销对象。
编译器总会为每个类合成一个析构函数。
当1,需要释放指针成员的资源时,2,需要执行某些特定工作时,必须自己定义析构函数。
12.确定在习题13.4中概略定义的NoName类是否需要析构函数,如果需要,实现它。
根据“三法则”需要显式定义析构函数,实现为:
NoName:
~NoName
deletepstring;
13.理解复制控制成员和构造函数的一个良好方式是定义一个简单类,该类具有这些成员,每个成员打印自己的名字:
structExmp1{
Exmp1(){std:
cout<
<
“Exmp1()”<
endl;
Exmp1(constExmp1&
)
{std:
“Exmp1(constExmp1&
)”<
//…
编写一个像Exmp1这样的类,给出复制控制成员和其他构造函数。
然后写一个程序,用不同方式使用Exmp1类型的对象:
作为非引用和引用形参传递,动态分配,放在容器中,等等,研究何时执行哪个构造函数和复制控制成员,可以帮助你融会贯通第理解这些概念。
//13.14_CopyControlMember.cpp:
定义控制台应用程序的入口点。
//
#include"
stdafx.h"
#include<
iostream>
vector>
classExmp
//constructor
Exmp()
{
std:
"
UsingExmp()."
<
//
//copyconstructor
Exmp(constExmp&
)
UsingExmp(constExmp&
)_copyconstructor."
//overloadoperator
Exmp&
operator=(constExmp&
UsingExmp&
)_overloadoperator."
//destructor
~Exmp()
Using~Exmp()."
voidfunc1(Exmpobj)//形参为Exmp的对象
}
voidfunc2(Exmp&
obj)//形参为Exmp对象的引用
Exmpfunc3()
Exmpobj;
returnobj;
//返回exmp对象
int_tmain(intargc,_TCHAR*argv[])
Exmpexmp1;
Exmpexmp2(exmp1);
exmp2=exmp2;
func1(exmp1);
func2(exmp1);
exmp1=func3();
Exmp*p=newExmp;
Exmp>
evec(3);
deletep;
system("
pause"
);
return0;
15.下面的代码中发生了多少次析构函数的调用?
voidfcn(constSales_item*trans,Sales_itemaccm)
Sales_itemitem1(*trans),item2(accm);
if(!
item1.same_isbn(item2))return;
if(item1.avg_price()<
=99)return;
elseif(item2.avg_price()<
3次,分别用在当函数执行完毕后的撤销非static的形参对象accm和局部对象item1,item2.
16.编写本节中描述的Message类。
classMessage
Message(conststd:
str="
"
):
contents(str){}
Message(constMessage&
Message&
operator=(constMessage&
~Message();
voidsave(Folder&
voidremove(Folder&
voidaddFldr(Folder*);
voidremFldr(Folder*);
stringcontents;
set<
Folder*>
folders;
voidput_Msg_in_Folders(conststd:
&
voidremove_Msg_from_Folders();
Message:
Message(constMessage&
m):
contents(m.contents),folders(m.folders)
put_Msg_in_Folders(folders);
voidMessage:
put_Msg_in_Folders(conststd:
&
rhs)
for(std:
const_iteratorbeg=rhs.begin();
beg!
=rhs.end();
++beg)
(*beg)->
addMsg(this);
Message&
Message:
operator=(constMessage&
rhs)
if(&
rhs!
=this)
remove_Msg_from_Folders();
contents=rhs.contents;
folders=rhs.folders;
put_Msg_in_Folders(rhs.folders);
remove_Msg_from_Folders()
const_iteratorbeg=folders.begin();
=folders.end();
remMsg(this);
~Message()
remove_Msg_from_Folders();
save(Folder&
fldr)
addFldr(&
fldr);
fldr.remMsg(this);
remove(Folder&
fldr)
remFldr(&
addFldr(Folder*fldr)
folders.insert(fldr);
remFldr(Folder*fldr)
folders.erase(fldr);
17.为message类增加与Folder的addMsg和remMsg操作类似的函数。
这些函数可以命名为addFldr和remFldr,应接受一个指向Folder的指针并将该指针插入到folders。
这些函数可为private的,因为它们将仅在Message类的实现中使用。
addFldr(Folder*fldr)
18.编写相应的Folder类。
该类应保存一个set<
Message*>
包含指向Message的元素。
classMessage;
classFolder
Folder(){}
Folder(constFolder&
Folder&
operator=(constFolder&
~Folder();
voidsave(Message&
voidremove(Message&
voidaddMsg(Message&
voidremMsg(Message&
messages;
voidput_Fldr_In_Messages(conststd:
voidremove_Fldr_Messages();
Folder:
Folder(constFolder&
f):
messages(f.messages)
put_Fldr_In_Messages(messages);
voidFolder:
put_Fldr_In_Messages(conststd:
for(std:
++beg)
addFldr(this);
Folder&
Folder:
operator=(constFolder&
=this)
remove_Fldr_Messages();
messages=rhs.messages;
put_Fldr_In_Messages(rhs.messages);
remove_Fldr_Messages()
for(std:
const_iteratorbeg=messages.begin();
beg!
=messages.end();
remFldr(this);
~Folder()
remove_Fldr_Messages();
save(Message&
msg)
addMsg(&
msg);
msg.addFldr(this);
remove(Message&
remMsg(&
msg.remFldr(this);
addMsg(Message&
messages.insert(mag);
remMsg(Message&
messages.erase(msg);
19.在Message类和Folder类中增加save和remove操作,这些操作应接受一个Folder,并将该Folder加入到指向这个Message的Folder集中(或从其中删除该Folder)。
操作还必须更新Folder以反映它指向该Message,这可以通过调用addMsg或remMsg完成。
20.对于HasPtr类的原始版本(依赖于复制控制的默认定义),描述下面代码中会发生什么:
inti=42;
HasPtrp1(&
i,42);
HasPtrp2=p1;
p2.get_ptr_val()<
endll;
输出42
p1.set_ptr_val(0);
endl;
输出0
21.如果给HasPtr类添加一个析构函数,用来删除指针成员,会发生什么?
如果这样,则撤销一个HasPtr对象时会删除其指针成员所指向的对象,从而使得当一个HasPtr对象被撤消后,其他由该对象复制而创建的HasPtr对象中的指针成员也无法使用。
22.什么是使用计数?
使用计数是复制控制成员中使用的编程技术。
将一个计数器与类指向的对象相关联,用于跟踪该类有多少个对象共享同一指针。
创建一个单独类指向共享对象并管理使用计数。
由构造函数设置共享对象的状态并将使用计数置为1。
每当由复制构造函数或赋值操作符生成一个新副本时,使用计数加1。
由析构函数撤销对象或作为赋值操作符的左操作数撤销对象时,使用计数减少1。
赋值操作符和析构函数检查使用计数是否已减至0,若是,则撤销对象。
23.什么是智能指针?
智能指针如何与实现普通指针行为的类相区别?
智能指针式一个行为类似指针但也提供其他功能的类。
这个类与实现普通指针行为的类区别在于:
智能指针通常接受指向动态分配对象的指针并负责删除该对象。
用户分配对象,但由智能指针类删除它,因此智能指针类需要实现赋值控制成员来管理指向共享对象的指针。
只有在撤销了指向共享对象的最后一个智能指针后,才能删除该共享对象。
使用计数就是实现智能指针类最常用的一个方式。
24.实现你自己的使用计数式HasPtr类的版本。
classU_Ptr
friendclassHasPtr;
int*ip;
size_tuse;
U_Ptr(int*p):
ip(p),use
(1){}
~U_Ptr(){deleteip;
classHasPtr
HasPtr(int*p,inti):
ptr(newU_Ptr(p),val(i){}
HasPtr(constHasPtr&
rhs):
ptr(rhs.ptr),val(rhs.val)
++ptr->
use;
HasPtr&
operator=(constHasPtr&
~HasPtr()
if(--ptr->
use==0)deleteptr;
int*get_ptr()const{returnptr->
ip;
intget_int()const{returnval;
voidset_ptr(int*p){ptr->
ip=p;
voidset_int(inti){val=i;
intget_ptr_val()const{return*ptr->
voidset_ptr_val(inti){*ptr->
ip=i;
U_Ptr*ptr;
intval;
HasPtr&
HasPtr:
operator=(constHasPtr&
++rhs.ptr->
if(--ptr->
use==0)
deleteptr;
ptr=rhs.ptr;
val=rhs.val;