C++Primer第4版习题解答十二章.docx
- 文档编号:5327780
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:17
- 大小:30.11KB
C++Primer第4版习题解答十二章.docx
《C++Primer第4版习题解答十二章.docx》由会员分享,可在线阅读,更多相关《C++Primer第4版习题解答十二章.docx(17页珍藏版)》请在冰豆网上搜索。
C++Primer第4版习题解答十二章
第十二章类和数据抽象
12.1编写一个名为person的类,表示人的名字和地址,使用string来保存每个元素。
答:
classperson
{
public:
person(stringpName,stringpAddress)
{
name=pName;
address=pAddress;
}
private:
stringname;
stringaddress;
};
12.2为person提供一个接收两个string参数的构造函数。
见第一题。
12.3提供返回名字和地址的操作。
这些函数应为const吗?
解释你的选择。
在public里添加成员函数:
stringget_name()const
{
returnname;
]
stringget_address()const
{
returnaddress;
]
这两个成员函数不应该修改其操作的对象的数据成员的值,应该声明为const类型。
12.4指明person的哪个成员应声明为public,哪个成员应声明为private。
解释。
数据成员name和address应为private,保证只能被类的成员所用,外界不可访问。
成员函数get_name()和get_address()应声明为public,为外界提供接口访问类的数据成员。
构造函数也应声明为public,以便初始化类的对象。
12.5C++类支持哪些访问标号?
在每个访问标号之后应定义哪种成员?
如果有的话,在类的定义中,一个访问标号可以出现在何处以及可出现多少次?
约束条件是什么?
有public,private,protect。
public后定义可被外界访问的接口,private后定义只能被本类成员函数使用的成员;protect后定义的成员称为受保护成员,只能由本类及本类的子类访问。
访问标号可以出现在任意成员定义之前且次数没有限制。
约束条件是:
每个访问标号指定了随后的成员定义级别,这个级别持续有效,直到下一个访问标号出现,或者看到类定义体的右花括号为止。
12.6用class关键字定义的类和用struct定义的类有什么不同。
默认访问标号不同,用struct关键字定义的,在第一个访问标号之前的成员是共有的,如果是用class关键字定义的,在第一个访问标号之前的成员是private成员。
12.7什么事封装?
为什么封装是有用的?
封装是一种将低层次的元素组合起来形成新的、高层次实体的技术。
例如,函数是封装的一种形式:
函数所执行的细节行为被封装在函数本身这个更大的实体中。
被封装的元素隐藏了它们的实现细节,可以调用一个函数但不能访问它所执行的语句,同样类也是一个封装的实体:
它代表若干成员的聚集,大多数类类型隐藏了实现该类型的成员。
封装隐藏了内部元素的实现细节,提供了优点:
避免类内部出现无意的可能破坏对象状态的用户级错误;在修改类的实现时不需要修改用户级代码,这些都很有用。
12.8将sales_item:
:
avg_price定义为内联函数。
inlinedoublesales_item:
:
avr_price()const
{
if(units_sole)
returnrevenue/units_sold;
elsereturn0;
}
12.9修改本节中给出的screen类,给出一个构造函数,根据屏幕的高度、宽度和内容的值来创建screen。
classScreen
{
public:
typedefstd:
:
string:
:
size_typeindex;
Screen(indexhei,indexwid,stringcontent)
{
contents=content;
height=hei;
width=wid;
}
private:
std:
:
stringcontents;
indexcursor;
indexheight,width;
};
12.10解释下述类中的每个成员:
classRecord{
typedefstd:
:
size_tsize;
Record():
byte_count(0){}
Record(sizes):
byte_count(s){}
Record(std:
:
stirngs):
name(s),byte_count(0){}
sizebyte_count;
std:
:
stringname;
public:
sizeget_count()const{returnbyte_count;}
std:
:
stringget_name()const{returnname;}
};
三个Record()函数是重载的三个构造函数,sizebyte_count;std:
:
stringname;这是两个private的数据成员,sizeget_count()const,std:
:
stringget_name()const这是两个public成员函数。
12.11定义两个类X和Y,X中有一个指向Y的指针,Y中有一个X类型的对象。
classY;
classX
{
Y*p;
};
classY{
Xxobj;
};
12.12解释类声明与类定义之间的差异。
何时使用类声明?
何时使用类定义?
类声明是不完全类型,只能以有限方式使用,不能定义该类型的对象,只能用于定义指向该类型的指针及引用,或者声明使用该类型作为形参类型或返回类型的函数。
类定义,一旦类被定义,我们就可以知道所有类的成员,以及存储该类的对象所需的存储空间。
在创建类的对象之前或者使用引用或指针访问类的成员之前必须定义类。
12.13扩展screen类以及包含move.set和display操作。
通过执行如下表达式来测试类:
myScreen.move(4,0).set(‘#’).display(cout);
//12.13_Screen.cpp:
定义控制台应用程序的入口点。
//
#include"stdafx.h"
#include
#include
usingnamespacestd;
classScreen
{
public:
typedefstd:
:
string:
:
size_typeindex;
Screen(indexhei,indexwid,conststring&content="")
{
cursor=0;
contents=content;
height=hei;
width=wid;
}
//add3newmembers
Screen&move(indexr,indexc)
{
indexrow=r*width;
cursor=row+c;
return*this;
}
Screen&set(charc)
{
contents[cursor]=c;
return*this;
}
Screen&display(ostream&os)
{
do_display(os);
return*this;
}
constScreen&display(ostream&os)const
{
do_display(os);
return*this;
}
private:
std:
:
stringcontents;
indexcursor;
indexheight,width;
voiddo_display(ostream&os)const
{
os< } }; int_tmain(intargc,_TCHAR*argv[]) { Screenmyscreen(5,5,"bbbbbbbbbb\ngggg\nfffffff\niiiiiiiiiiiiiiiiii\nmmmmmmmm\n"); myscreen.move(4,0).set('#').display(cout); system("pause"); return0; } 12.14通过this指针引用成员虽然合法,但却是多余的。 讨论显示地使用this指针访问成员的优缺点。 优点: 当需要将一个对象作为整体引用而不是引用对象的一个成员时,使用this,则该函数返回对调用该函数的对象的引用。 可以非常明确地指出访问的是调用该函数的对象的成员,且可以在成员函数中使用与数据成员同名的形参。 缺点: 不必要使用,代码多余。 12.15列出在类作用域中的程序文本部分。 类的定义体;在类外定义的成员函数: 形参表、成员函数体,但不包含函数的返回类型。 12.16如果定义get_cursor时,将会发生什么? IndexScreen: : get_cursor()const{returncursor;} 编译错误,index是在Screen类的作用域之外的,在类外没有定义index类型。 12.17如果将Screen类中的类型别名放到类中的最后一行,将会发生什么? 发生编译错误,因为Index的使用出现在了其定义之前。 12.18解释下述代码,指出每次使用Type或initVal时用到的是哪个名字定义。 如果存在错误,说明如何改正。 Typedefstringtype; TypeinitVal(); classExercise{ public: //... typedefdoubleType; TypesetVal(Type); TypeinitVal(); private: intval; }; TypeExercise: : setVal(Typeparm){ val=parm+initVal(); } 成员函数setVal的定义有错。 进行必要的修改以便类Exercise使用全局的类型别名Type和全局函数initVal. 答: 在类的定义体内,两个成员函数的返回类型和setVal的形参使用的Type都是类内部定义的类型别名Type; 在类外的成员函数setVal的定义体内,形参的类型Type是Exercise类内定义的类型别名Type,返回类型的Type使用的是全局的类型别名Type。 在steVal的定义体内使用的initVal函数,使用的是Exercise类的成员函数。 在SetVal的函数定义有错: 1.缺少返回类型;2.此函数的返回类型是string类型的,而类中声明的函数返回类型是double类型的,不合法的函数重载,可以改为: Exercise: : TypeExercise: : setVal(Typeparm){ val=parm+initVal(); returnval; } 把setVal修改为如下,可保证类Exercise使用全局的类型别名Type和全局函数initVal. Typedefstringtype; TypeinitVal(); classExercise{ public: //... TypesetVal(Type); private: Typeval; }; TypeExercise: : setVal(Typeparm) { val=parm+initVal(); returnval; } 12.19提供一个或多个构造函数,允许该类的用户不指定数据成员的初始值或指定所有数据成员的初始值: classNoName{ public: //constructor(s)gohere... private: std: : string*pstring; intival; doubledval; }; 解释如何确定需要多少个构造函数以及它们应该接受什么样的形参。 实参决定调用哪个构造函数,所以实参的种类数决定构造函数的数目,因此此题至少需要两个构造函数,一个是默认没有参数的构造函数,一个是带有三个形参的构造函数,类型要与三个数据成员的类型匹配。 构造函数: classNoName{ public: //constructor(s)gohere... NoName(){} NoName(std: : string*ps,intiv,doubledv) { pstring=ps; ival=iv; dval=dv; } private: std: : string*pstring; intival; doubledval; }; 12.20从下述抽象中选择一个(或一个自己定义的抽象),确定类中需要什么数据,并提供适当的构造函数集。 解释你的决定: (a)Book(b)Date(c)Employee (d)Vehicle(e)Object(f)Tree (b)Date: 需要年: intyear;月: intmonth;日: intday 构造两个构造函数,一个默认构造函数,用于创建空的Date对象,一个带有三个形参,用于创建指定日期的对象。 构造函数为: classDate { public: Date(){} Date(inty,intm,intd) { year=y;month=m;day=d; } private: intyear,month,day; }; 12.21使用构造函数初始化列表编写类的默认构造函数,该类包含如下成员: 一个conststring,一个int,一个double*和一个ifstream&。 初始化string来保存类的名字。 classX { public: X(): cStr(“classname”),iVal(0),pd(0){} private: conststringcStr; intiVal; double*pd; ifstream&iFs; }; 12.22下面的初始化式有错误。 找出并改正错误。 structX{ X(inti,intj): base(i),rem(base%j){} intrem,base; }; structX的定义中,rem先被定义,先被初始化,在构造函数初始化列表中的初始化顺序是先初始化rem,而此时rem用还没有被初始化的base来初始化rem,会产生runtimeerror. 改正为: structX{ X(inti,intj): rem(i%j),base(i){} 12.23假定有个命名为NoDefault的类,该类有一个接受一个int的构造函数,但没有默认构造函数。 定义有一个NoDefault类型成员的类C。 为类C定义默认构造函数。 classc{ public: c(): i(0),Ndf(i){} private: inti; NoDefaultNdf; }; 12.24上面的Sales_item定义了两个构造函数,其中之一有一个默认实参对应其单个string形参,使用该Sales_item版本,确定用哪个构造函数来初始化下述的每个变量,并列出对象中数据成员的值。 Sales_itemfirst_item(cin); 用的是第二个即: Sales_item(std: : istream&is);对象的数据成员的值为: isbn的值为cin输入的字符串。 intmain(){ Sales_itemnext; Sales_itemlast(“9-999-99999-9”); } 这两个用的是第一个即: 有默认实参的构造函数,next对象的数据成员的值为: isbn空串,units_sold值为0,revenue值为0.0。 last对象的数据成员的值为: isbn为”9-999-99999-9”,units_sold值为0,revenue值为0.0。 12.25逻辑上讲,我们可能希望将cin作为默认实参提供给接受一个istream&形参的构造函数。 编写使用cin作为默认实参的构造函数声明。 Sales_item(std: : istream&is=std: : cin); 12.26接受一个string和接受一个istream&的构造函数都具有默认实参是合法的吗? 如果不是,为什么? 不合法,如果二者都具有默认实参,则造成了默认构造函数的重复定义,当定义一个Sales_item的对象而没有给出用于构造函数的实参时,将因为无法确定使用哪个构造函数而出错。 12.27下面的陈述中哪个是不正确的(如果有的话)? 为什么? (a)类必须提供至少一个构造函数。 (b)默认构造函数的形参列表中没有形参。 (c)如果一个类没有有意义的默认值,则该类不应该提供默认构造函数。 (d)如果一个类没有定义默认构造函数,则编译器会自动生成一个,同时将每个数据成员初始化为相关类型的默认值。 (a)不提供构造函数时,由编译器合成一个默认构造函数。 (b)错了,为所有形参提供了默认实参的构造函数也定义了默认构造函数,而这样的构造函数形参列表中是有形参的。 (c)不正确,如果一个类没有默认的构造函数,而只有自己写的构造函数,在编译器需要隐式使用默认构造函数时,这个类就不能使用,编译不通过,所以如果一个类定义了其他的构造函数,通常也应该提供一个默认的构造函数。 (d)不正确,1,当一个类中自己定义了构造函数时,编译器就不会自动合成一个默认构造函数了,2,编译器不是将每个数据成员初始化为相关类型的默认值,而是使用与变量初始化相同的规则来初始化成员: 类类型的成员执行各自的默认构造函数进行初始化,内置和复合类型的成员,只对定义在全局作用域中的对象才初始化。 12.28解释一下接受一个string的Sales_item构造函数是否应该为explicit.将构造函数设置为explicit的好处是什么? 缺点是什么? 应该声明为explicit,如果不声明为explicit,编译器可以使用此构造函数进行隐式类型转换,即将一个string类型的对象转换为Sales_item对象,这种行为时容易发生语义错误的。 好处是: 可以防止构造函数的隐式类型转换而带来的错误,缺点是,当用户确实需要进行相应的类型转换时,不能依靠隐式类型转换,必须显示地创建临时对象。 12.29解释在下面的定义中所发生的操作。 stringnull_isbn=“9-999-99999-9”; Sales_itemnull1(null_isbn); Sales_itemnull(“9-999-99999-9”); 1,调用接收一个C风格字符串形参的string构造函数,创建一个临时的string对象,然后调用string类的复制构造函数,将null_isbn初始化为该该临时对象的副本。 2,使用string的对象null_isbn为实参,调用Sales_item类的构造函数创建Sales_item对象null1. 3,使用接受一个C风格字符串形参的string类的构造函数,生成一个临时string对象,然后用这个临时对象作为实参,调用Sales_item类的构造函数来创建Sales_item类的对象null. 12.30编译如下代码: f(constvector intmain(){ vector f(v2);//shoulebeok f(42);//shoulebeanerror return0; } 基于对f的第二个调用中出现的错误,我们可以对vector构造函数做出什么推断? 如果该调用成功了,那么你能得出什么结论? 可以做出以下推断: vector中没有定义接受一个int型参数的构造函数,或者即使定义了接受一个int型参数的构造函数,该函数也被声明为了explicit。 如果调用成功了,则说明vector中定义了可以接受一个int型参数的构造函数。 12.31pair的数据成员为Public,然而下面这段代码却不能编译,为什么? pair 可能是因为pair类定义了构造函数,此时只能使用构造函数初始化成员,可改为: pair 12.32什么是友元函数? 什么是友元类? 被指定为某类的友元的函数称为该类的友元函数。 被指定为某类的友元的类称为授予友元关系的那个类的友元类。 12.33什么时候友元是有用的? 讨论使用友元的优缺点。 在需要允许某些特定的非成员函数访问一个类的私有成员,而同时仍阻止一般的访问的情况下,友元机制是个有用的东西。 优点: 可以灵活地实现需要访问若干类的私有或受保护成员才能完成的任务,便于与其他不支持类的语言进行混合编程;通过使用友元函数重载可以更自然第使用C++语言的I/O流库。 缺点: 一个类将对非公有成员的访问权授予其他的函数或类,会破坏该类的封装性,降低该类的可靠性和可维护性。 12.34定义一个将两个Sales_item对象相加的非成员函数。 首先将此函数定义: Sales_itemadd2obj(constSales_item&obj1,constSales_item&obj2) { if(! obj1.same_isbn(obj2)) returnobj1; Sales_itemtemp; temp.isbn=obj1.isbn; temp.units_sold=obj1.units_sold+obj2.units_sold; temp.revenue=obj1.revenue+obj2.revenue; returntemp; } 然后再类Sales_item中将此函数声明为友元: friendSales_itemadd2obj(constSales_item&,constsales_item&); 12.35定义一个非成员函数,读取一个istream并将读入的内容存储到一个Sales_item中。 stream&read(istream&is,Sales_item&obj) {
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Primer 习题 解答 十二