Chapter 14 C++中的代码重用
1)包含成员对象的类¶
【1】引入:当某些类包含了其他类的成员对象时,应当如何调用?
【2】简介:模板特性声明对象时必须加上特定的具体数据类型
C++ | |
---|---|
1 2 |
|
【3】设计方式:用于建立 has-a 关系的技术是组合(包含),即:创建一个包含其他类对象的类
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
【4】示例解析:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
(2)关键字explicit的用法:关闭隐式自动转换 ![[Pasted image 20230805095933.png]]
(3)c++及约束: 【1】初始化列表的功能(要多使用!)
[1] 初始化内置类型的成员
[2] 初始化派生类对象的基类部分
C++ 1
Queue :: Queue(int qs) : qsize(qs) {...}
ps:对于派生类而言 对于要继承的对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数; 对于成员对象,构造函数则使用成员名
C++ 1
hasDMA :: hasDMA(const hasDMA& hs) : baseDMA(hs) {...}
C++ 1
Student(const char* str, const double* pd, int n) : name(str),scores(pd,n) {}
【2】初始化顺序
当初始化列表包含多个项目时,这些项目被初始化的顺序为它们被声明的顺序,而不是它们在初始化列表中的顺序
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14class Student { private: typedef std::valarray<double> ArrayDb; std::string name; // contained object ArrayDb scores; // contained object // private method for scores output std::ostream & arr_out(std::ostream & os) const; public: Student(const char* str, const double* pd, int n) : scores(pd,n),name(str) {} ... } 此时:name成员仍将首先被初始化,因为它在类定义中首先被声明!
【3】使用被包含对象的接口
被包含对象的接口不是共有的,但是可以在类方法中使用它 eg1:
eg2:
C++ 1 2 3 4 5 6double Student::Average() const { if( scores.size() > 0 ) return (scores.sum()/scores.size() ); else return 0; } // scores是Student类的成员,scores还是一个valarray对象,所以它可以调用valarray类的成员函数scores.size() & scores.sum()
C++ 1 2 3 4 5 6 7 8ostream& operator<<(ostream& os,const Student& stu) { os << "Scores for " << stu.name <<": \n"; ...... } (1) stu.name是一个string对象,所以它将调用 operator<<(ostream&,const string&) ,而这已经在string类中了; (2) operator<<(ostream& os,const Student& stu) 应当声明为Student类的友元函数,这样才能访问Student类中的name成员。
2)私有继承¶
子对象 (subobject):通过继承or包含添加的对象 多重继承(muitiple inheritance , MI):使用多个基类的继承 隐式向上转换(implicit upcasting):无需进行显示类型转换,就可以将 基类指针 or 引用 指向派生类对象
3)多重继承¶
4)类模板¶
(1)引入:¶
c++的类模板为生成通用的类声明提供了一种更好的方法,模板提供参数化(parameterized)类型,即:能够将类型名作为参数传递给接收方来建立类或函数
(2)定义类模板:¶
【1】开头:
C++ | |
---|---|
1 |
|
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
(3)使用模板类¶
[1] 必须请求实例化,方法是使用所需要的具体类型替换泛型名
[2] 泛型标识符——称为类型参数(type parameter)。比如上面的Type。它们类似于变量,但赋给它们的只能是类型,必须显式提供类型!
C++ 1 2Stack<int> ker; // a stack of ints Stack<string> col; // a stack of string objects
ps: 其余使用细则函数模板基本没区别! 结合上面的实例自行品味!
C++ 1 2 3 4 5template <class T> void simple(T t) {cout << t <<endl;} ... simple(2); // generate void simple(int) simple("two"); // generate void simple(const char*)
(4)数组模板实例and非类型参数¶
示例:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
C++ | |
---|---|
1 |
|
(2)表达式参数的限制:
【1】必须是:整型、枚举、引用、指针 如:double m;不合法!
如:double* rm;合法!【2】模板代码不能修改参数的值 or 地址 如:不能出现 n++ 或 &n 等表达式
【3】实例化模板时,用作表达式参数的值必须是常量表达式
(5)模板多功能性¶
【1】可以递归使用模板:
C++ 1 2 3ArrayTP< ArrayTP<int,5> , 10 > twodee; 生成二维数组:包含10个元素的数组,每个元素都是一个包含5个int元素的数组
【2】使用多个类型参数:
注意这里的声明:
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59希望可以保存两种值(键值对),创建并使用Pair模板: // pairs.cpp -- defining and using a Pair template #include <iostream> #include <string> template <class T1, class T2> class Pair { private: T1 a; T2 b; public: T1 & first(); T2 & second(); T1 first() const { return a; } T2 second() const { return b; } Pair(const T1 & aval, const T2 & bval) : a(aval), b(bval) { } Pair() {} }; template<class T1, class T2> T1 & Pair<T1,T2>::first() { return a; } template<class T1, class T2> T2 & Pair<T1,T2>::second() { return b; } int main() { using std::cout; using std::endl; using std::string; Pair<string, int> ratings[4] = { Pair<string, int>("The Purpled Duck", 5), Pair<string, int>("Jaquie's Frisco Al Fresco", 4), Pair<string, int>("Cafe Souffle", 5), Pair<string, int>("Bertie's Eats", 3) }; int joints = sizeof(ratings) / sizeof (Pair<string, int>); cout << "Rating:\t Eatery\n"; for (int i = 0; i < joints; i++) cout << ratings[i].second() << ":\t " << ratings[i].first() << endl; cout << "Oops! Revised rating:\n"; ratings[3].first() = "Bertie's Fab Eats"; ratings[3].second() = 6; cout << ratings[3].second() << ":\t " << ratings[3].first() << endl; // std::cin.get(); return 0; }
C++ 1
template <class T1, class T2>
(6)模板的具体化¶
【1】隐式实例化 (implicit instantiation):
C++ | |
---|---|
1 |
|
C++ | |
---|---|
1 2 |
|
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
(7)模板类和友元¶
【1】引入分类:
非模板友元; 约束 (bound) 模板友元; 非约束 (unbound) 模板友元;
【2】模板类的非模板友元函数:
(1)一旦声明,使得该函数成为模板所有实例化的友元:
C++ 1 2 3 4 5 6 7 8 9template <class T> class HasFriend { public: friend void counts(); // friend to all HasFriend instantiation ... } 例如:它将是类HasFriend<int> 和 HasFriend<string>
(2)要为友元函数提供模板类参数,则必须指明具体化
C++ 1 2 3 4 5 6 7 8 9 10template <class T> class HasFriend { public: friend void report(HasFriend<T>& ); // valid! HasFriend<T> 是实例 friend void report(HasFriend& ); // invalid! HasFriend 是抽象,非具体! 假设这里T是int:带HasFriend<int>参数的report()将成为HasFriend<int> 类的友元 假设这里T是double:带HasFriend<double>参数的report()将成为HasFriend<double> 类的友元 }
(3)要使用的友元全部都要定义显式具体化
C++ 1 2 3 4 5 6比如上例的后续调用需要使用:HasFriend<int> 与 HasFriend<double> 则需要类外定义: void report(HasFriend<int>& ) {...}; 与 void report(HasFriend<double>& ) {...};
【3】模板类的约束(bound)模板友元函数:
aim:使得友元函数本身成为模板
[1] 首先在类定义的前面声明每个模板函数
C++ 1 2template <typename T> void counts(); template <typename T> void report(T& );
[2] 其次在函数中将模板声明为友元(声明具体化)
C++ 1 2 3 4 5 6 7 8 9 10 11template <typename TT> class HasFriend2 { ... friend void counts<TT>(); friend void report<>(HasFriend2<TT>& ); }; 声明中的<>指出这是模板具体化: 对于 report() ,<>可以为空,因为可以从函数参数推断出模板类型参数:HasFriend2<TT>,自动明白是report< HasFriend2<TT> >(HasFriend2<TT>& ); 对于 counts() ,由于该函数没有参数,所以必须使用TT来指明具体化
[3] 为友元提供模板定义
程序说明:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
【4】模板类的非约束(unbound)模板友元函数:
aim:通过在类内部声明模板,可以创建非约束友元函数,即:每个函数具体化都是每个类具体化的友元
1)格式:
C++ | |
---|---|
1 2 3 4 5 6 |
|
C++ | |
---|---|
1 |
|
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
(8)模板别名¶
【1】使用 typedef:
格式:typedef To_be_trans_name Re_name ;
例子:
C++ | |
---|---|
1 2 3 4 5 6 7 |
|
【2】使用 template:略
【3】使用 using:略