C++Primer 第13章复制控制课后习题答案.docx

上传人:b****1 文档编号:152715 上传时间:2023-04-28 格式:DOCX 页数:22 大小:35.37KB
下载 相关 举报
C++Primer 第13章复制控制课后习题答案.docx_第1页
第1页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第2页
第2页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第3页
第3页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第4页
第4页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第5页
第5页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第6页
第6页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第7页
第7页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第8页
第8页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第9页
第9页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第10页
第10页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第11页
第11页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第12页
第12页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第13页
第13页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第14页
第14页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第15页
第15页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第16页
第16页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第17页
第17页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第18页
第18页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第19页
第19页 / 共22页
C++Primer 第13章复制控制课后习题答案.docx_第20页
第20页 / 共22页
亲,该文档总共22页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

C++Primer 第13章复制控制课后习题答案.docx

《C++Primer 第13章复制控制课后习题答案.docx》由会员分享,可在线阅读,更多相关《C++Primer 第13章复制控制课后习题答案.docx(22页珍藏版)》请在冰点文库上搜索。

C++Primer 第13章复制控制课后习题答案.docx

C++Primer第13章复制控制课后习题答案

第十三章复制控制

1.什么是复制构造函数?

何时使用它?

只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数叫复制构造函数。

copyconstructorwillbeusedunderthesesituations:

根据一个同类型的对象显式或隐式初始化一个对象;

复制一个对象,将它作为实参传给一个函数;

从函数返回时复制一个对象;

初始化顺序容器中的对象;

根据元素初始化式列表初始化数组元素。

2.下面第二个初始化不能编译。

可以从vector的定义得出什么推断?

vectorv1(42);

vectorv2=42;

若能编译成功说明,这是个复制初始化,创建v2时,首先调用接受一个int型形参的vector构造函数,创建一个临时vector对象,然后再调用复制构造函数用这个临时对象来初始化v2。

现在不能编译,说明vector没有定义复制构造函数。

3.假定Point为类类型,该类类型有一个复制构造函数,指出下面程序段中每一个使用了复制构造函数的地方:

Pointglobal;

Pointfoo_bar(Pointarg)//调用此函数时,将实参对象的副本传递给形参Point的对象arg

{

Pintlocal=arg;//调用复制构造函数,将局部对象local初始化为形参arg的副本。

Point*heap=newPoint(global);//调用复制构造函数用全局对象global来初始化

//Point对象*heap

*heap=local;

Pointpa[4]={local,*heap};//使用数组初始化列表来初始化数组的每个元素。

return*heap;//从函数返回Point对象*heap的副本

}

 

4.对于如下的类的简单定义,编写一个复制构造函数所有成员。

复制pstring指向的对象而不是复制指针。

structNoName{

NoName():

pstring(newstd:

:

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;

}

private:

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:

:

cout<<“Exmp1(constExmp1&)”<

:

endl;}

//…

};

编写一个像Exmp1这样的类,给出复制控制成员和其他构造函数。

然后写一个程序,用不同方式使用Exmp1类型的对象:

作为非引用和引用形参传递,动态分配,放在容器中,等等,研究何时执行哪个构造函数和复制控制成员,可以帮助你融会贯通第理解这些概念。

//13.14_CopyControlMember.cpp:

定义控制台应用程序的入口点。

//

#include"stdafx.h"

#include

#include

classExmp

{

public:

//constructor

Exmp()

{

std:

:

cout<<"UsingExmp()."<

:

endl;

}

//

//copyconstructor

Exmp(constExmp&)

{

std:

:

cout<<"UsingExmp(constExmp&)_copyconstructor."<

:

endl;

}

//overloadoperator

Exmp&operator=(constExmp&)

{

std:

:

cout<<"UsingExmp&operator=(constExmp&)_overloadoperator."<

:

endl;

return*this;

}

//destructor

~Exmp()

{

std:

:

cout<<"Using~Exmp()."<

:

endl;

}

};

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;

std:

:

vectorevec(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()<=99)return;

//…

}

3次,分别用在当函数执行完毕后的撤销非static的形参对象accm和局部对象item1,item2.

 

16.编写本节中描述的Message类。

classMessage

{

public:

Message(conststd:

:

string&str=""):

contents(str){}

Message(constMessage&);

Message&operator=(constMessage&);

~Message();

voidsave(Folder&);

voidremove(Folder&);

voidaddFldr(Folder*);

voidremFldr(Folder*);

private:

std:

:

stringcontents;

std:

:

setfolders;

voidput_Msg_in_Folders(conststd:

:

set&);

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:

:

set&rhs)

{

for(std:

:

set:

:

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);

}

return*this;

}

voidMessage:

:

remove_Msg_from_Folders()

{

for(std:

:

set:

:

const_iteratorbeg=folders.begin();beg!

=folders.end();++beg)

{

(*beg)->remMsg(this);

}

}

Message:

:

~Message()

{

remove_Msg_from_Folders();

}

voidMessage:

:

save(Folder&fldr)

{

addFldr(&fldr);

fldr.remMsg(this);

}

voidMessage:

:

remove(Folder&fldr)

{

remFldr(&fldr);

fldr.remMsg(this);

}

voidMessage:

:

addFldr(Folder*fldr)

{

folders.insert(fldr);

}

voidMessage:

:

remFldr(Folder*fldr)

{

folders.erase(fldr);

}

17.为message类增加与Folder的addMsg和remMsg操作类似的函数。

这些函数可以命名为addFldr和remFldr,应接受一个指向Folder的指针并将该指针插入到folders。

这些函数可为private的,因为它们将仅在Message类的实现中使用。

voidMessage:

:

addFldr(Folder*fldr)

{

folders.insert(fldr);

}

voidMessage:

:

remFldr(Folder*fldr)

{

folders.erase(fldr);

}

 

18.编写相应的Folder类。

该类应保存一个set,包含指向Message的元素。

classMessage;

classFolder

{

public:

Folder(){}

Folder(constFolder&);

Folder&operator=(constFolder&);

~Folder();

voidsave(Message&);

voidremove(Message&);

voidaddMsg(Message&);

voidremMsg(Message&);

private:

std:

:

setmessages;

voidput_Fldr_In_Messages(conststd:

:

set&);

voidremove_Fldr_Messages();

};

Folder:

:

Folder(constFolder&f):

messages(f.messages)

{

put_Fldr_In_Messages(messages);

}

voidFolder:

:

put_Fldr_In_Messages(conststd:

:

set&rhs)

{

for(std:

:

set:

:

const_iteratorbeg=rhs.begin();beg!

=rhs.end();++beg)

(*beg)->addFldr(this);

}

Folder&Folder:

:

operator=(constFolder&rhs)

{

if(&rhs!

=this)

{

remove_Fldr_Messages();

messages=rhs.messages;

put_Fldr_In_Messages(rhs.messages);

}

return*this;

}

voidFolder:

:

remove_Fldr_Messages()

{

for(std:

:

set:

:

const_iteratorbeg=messages.begin();

beg!

=messages.end();++beg)

{

(*beg)->remFldr(this);

}

}

Folder:

:

~Folder()

{

remove_Fldr_Messages();

}

voidFolder:

:

save(Message&msg)

{

addMsg(&msg);

msg.addFldr(this);

}

voidFolder:

:

remove(Message&msg)

{

remMsg(&msg);

msg.remFldr(this);

}

voidFolder:

:

addMsg(Message&msg)

{

messages.insert(mag);

}

voidFolder:

:

remMsg(Message&msg)

{

messages.erase(msg);

}

 

19.在Message类和Folder类中增加save和remove操作,这些操作应接受一个Folder,并将该Folder加入到指向这个Message的Folder集中(或从其中删除该Folder)。

操作还必须更新Folder以反映它指向该Message,这可以通过调用addMsg或remMsg完成。

voidMessage:

:

save(Folder&fldr)

{

addFldr(&fldr);

fldr.remMsg(this);

}

voidMessage:

:

remove(Folder&fldr)

{

remFldr(&fldr);

fldr.remMsg(this);

}

 

20.对于HasPtr类的原始版本(依赖于复制控制的默认定义),描述下面代码中会发生什么:

inti=42;

HasPtrp1(&i,42);

HasPtrp2=p1;

cout<

p1.set_ptr_val(0);

cout<

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

{

public:

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->ip;}

voidset_ptr_val(inti){*ptr->ip=i;}

private:

U_Ptr*ptr;

intval;

};

HasPtr&HasPtr:

:

operator=(constHasPtr&rhs)

{

++rhs.ptr->use;

if(--ptr->use==0)

deleteptr;

ptr=rhs.ptr;

val=rhs.val;

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2