C++Primer 第13章复制控制课后习题答案.docx
- 文档编号:22839092
- 上传时间:2023-04-28
- 格式:DOCX
- 页数:22
- 大小:35.37KB
C++Primer 第13章复制控制课后习题答案.docx
《C++Primer 第13章复制控制课后习题答案.docx》由会员分享,可在线阅读,更多相关《C++Primer 第13章复制控制课后习题答案.docx(22页珍藏版)》请在冰豆网上搜索。
C++Primer第13章复制控制课后习题答案
第十三章复制控制
1.什么是复制构造函数?
何时使用它?
只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数叫复制构造函数。
copyconstructorwillbeusedunderthesesituations:
根据一个同类型的对象显式或隐式初始化一个对象;
复制一个对象,将它作为实参传给一个函数;
从函数返回时复制一个对象;
初始化顺序容器中的对象;
根据元素初始化式列表初始化数组元素。
2.下面第二个初始化不能编译。
可以从vector的定义得出什么推断?
vector
vector
若能编译成功说明,这是个复制初始化,创建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: : vector 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: : set 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 { 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 classMessage; classFolder { public: Folder(){} Folder(constFolder&); Folder&operator=(constFolder&); ~Folder(); voidsave(Message&); voidremove(Message&); voidaddMsg(Message&); voidremMsg(Message&); private: std: : set 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 { 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;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+Primer 第13章复制控制课后习题答案 Primer 13 复制 控制 课后 习题 答案