星球狂想战队|一起来学C++:C++中的代码重用( 三 )


typedefstd::valarrayArrayDb;这样 , 在以后的代码中便可以使用表示ArrayDb , 而不是std::valarray , 因此类方法和友元函数可以使用ArrayDb类型 。 将该typedef放在类定义的私有部分意味着可以在Student类的实现中使用它 , 但在Student类外面不能使用 。
请注意关键字explicit的用法:
explicitStudent(conststd::string&s):name(s),scores(){}explicitStudent(intn):name("Nully"),scores(n){}本书前面说过 , 可以用一个参数调用的构造函数将用作从参数类型到类类型的隐式转换函数;但这通常不是好主意 。 在上述第二个构造函数中 , 第一个参数表示数组的元素个数 , 而不是数组中的值 , 因此将一个构造函数用作int到Student的转换函数是没有意义的 , 所以使用explicit关闭隐式转换 。 如果省略该关键字 , 则可以编写如下所示的代码:
Studentdoh("Homer",10);//store"Homer",createarrayof10elementsdoh=5;//resetnameto"Nully",resettoemptyarrayof5elements在这里 , 马虎的程序员键入了doh而不是doh[0] 。 如果构造函数省略了explicit , 则将使用构造函数调用Student(5)将5转换为一个临时Student对象 , 并使用“Nully”来设置成员name的值 。 因此赋值操作将使用临时对象替换原来的doh值 。 使用了explicit后 , 编译器将认为上述赋值运算符是错误的 。
C++和约束
C++包含让程序员能够限制程序结构的特性——使用explicit防止单参数构造函数的隐式转换 , 使用const限制方法修改数据 , 等等 。 这样做的根本原因是:在编译阶段出现错误优于在运行阶段出现错误 。
1.初始化被包含的对象构造函数全都使用您熟悉的成员初始化列表语法来初始化name和score成员对象 。 在前面的一些例子中 , 构造函数用这种语法来初始化内置类型的成员:
Queue::Queue(intqs):qsize(qs){...}//initializeqsizetoqs上述代码在成员初始化列表中使用的是数据成员的名称(qsize) 。 另外 , 前面介绍的示例中的构造函数还使用成员初始化列表初始化派生对象的基类部分:
hasDMA::hasDMA(consthasDMA&hs):baseDMA(hs){...}对于继承的对象 , 构造函数在成员初始化列表中使用类名来调用特定的基类构造函数 。 对于成员对象 , 构造函数则使用成员名 。 例如 , 请看程序清单14.1的最后一个构造函数:
Student(constchar*str,constdouble*pd,intn):name(str),scores(pd,n){}因为该构造函数初始化的是成员对象 , 而不是继承的对象 , 所以在初始化列表中使用的是成员名 , 而不是类名 。 初始化列表中的每一项都调用与之匹配的构造函数 , 即name(str)调用构造函数string(constchar*) , scores(pd,n)调用构造函数ArrayDb(constdouble*,int) 。
如果不使用初始化列表语法 , 情况将如何呢?C++要求在构建对象的其他部分之前 , 先构建继承对象的所有成员对象 。 因此 , 如果省略初始化列表 , C++将使用成员对象所属类的默认构造函数 。
初始化顺序
当初始化列表包含多个项目时 , 这些项目被初始化的顺序为它们被声明的顺序 , 而不是它们在初始化列表中的顺序 。 例如 , 假设Student构造函数如下:
Student(constchar*str,constdouble*pd,intn):scores(pd,n),name(str){}则name成员仍将首先被初始化 , 因为在类定义中它首先被声明 。 对于这个例子来说 , 初始化顺序并不重要 , 但如果代码使用一个成员的值作为另一个成员的初始化表达式的一部分时 , 初始化顺序就非常重要了 。
2.使用被包含对象的接口被包含对象的接口不是公有的 , 但可以在类方法中使用它 。 例如 , 下面的代码说明了如何定义一个返回学生平均分数的函数:
doubleStudent::Average()const{if(scores.size()>0)returnscores.sum()/scores.size();elsereturn0;}上述代码定义了可由Student对象调用的方法 , 该方法内部使用了valarray的方法size()和sum() 。 这是因为scores是一个valarray对象 , 所以它可以调用valarray类的成员函数 。 总之 , Student对象调用Student的方法 , 而后者使用被包含的valarray对象来调用valarray类的方法 。


推荐阅读