前一章节 说明的是公有继承,其实还有 包含 、私有继承 和保护继承 ,它们实现了 has-a
关系,即新的类将包含另一个类的对象
包含
首先了解一下valarray
类,是由头文件valarray
支持的。这个类用于处理 数值(或具有类似特性的类),支持数组等操作。valarray
被定义为一个模板类,以便能够处理不同的数据类型。
模板特性意味着声明对象时,必须指定具体的数据类型。因此,使用valarray
类来声明一个对象时,需要在标识符valarray
后面加上一对尖括号,并在其中包含所需的数据类型
1 2 3 4 5 6 7 valarray<int > q_values; valarray<double > weight; valarray<int > v2 (8 ) ; valarray<int > v3 (10 , 8 ) ; double gpa[5 ] = {3.1 , 3.5 , 3.8 , 2.9 , 3.3 };valarray<double > v4 (gpa, 4 ) ;
首先声明一个类的定义 studentc.h
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 #ifndef STUDENTC_H_ #define STUDENTC_H_ #include <iostream> #include <string> #include <valarray> using namespace std ;class Student { private : typedef valarray<double > ArrayDb; string name; ArrayDb scores; ostream & arr_out (ostream & os) const ; public : Student() : name("Null Student" ), scores() {} explicit Student(const string & s) : name(s), scores() {} explicit Student(int n) : name("Nully"), scores(n) {} Student(const string & s, int n) : name(s), scores(n) {} Student(const string & s, const ArrayDb & a) : name(s), scores(a) {} Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) {} ~Student() {} double Average () const ; const string & Name () const ; double & operator [](int i); double operator [](int i) const ; friend istream & operator >>(istream & is, Student & stu); friend istream & getline (istream & is, Student & stu) ; friend ostream & operator <<(ostream & os, const Student & stu); }; #endif
这里用到了 explicit
,它的作用是:用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换
explicit
关键字只能用于类内部的构造函数声明上
explicit
关键字作用于单个参数的构造函数
其次,string
、valarray
都算是被包含的类,被包含对象的接口不是公有的,但可以在类方法中使用它(这个好理解,就是 string
、valarray
的函数执行范围在 student
内部一样)
私有继承
使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。而使用公有继承,基类的公有方法将成为派生类的公有方法,基类使用 private
修饰
1 2 3 4 5 class Student : private string , private valarray<double >{ public : ... };
使用私有继承时,只能在派生类的方法中使用基类的方法。但有时候可能希望基类工具是公有的。可以使用对象来调用方法也能 使用 类名和作用域解析运算符 来调用方法
1 2 3 4 5 6 7 8 9 10 11 double Student::Average () const { if (scores.size () > 0 ) return scores.sum()/scores.size (); else return 0 ; } double Student::Average () const { if (ArrayDb:size () > 0 ) return ArrayDb::sum()/ArrayDb::size (); else return 0 ; }
如果要使用基类对象本身呢?可以通过强制转换,因为继承类就是从基类派生出来的,所以可以强制转换
1 2 3 4 const string & Student::Name () const { return (const string &) *this ; }
这里指向用于调用该方法的 Student
对象中的继承而来的 string
对象
如果要访问基类的友元函数呢?用类名显式地限定函数名不适合于友元函数,这是因为友元不属于类。然而,可以通过显式地转换为基类来调用正确的函数
1 2 3 4 ostream & operator << (ostream & os, const Student & stu) { os << "Scores for " << (const String &) stu << ":\n" ; }
在私有继承中,在不进行显式类型转换的情况下,不能将指向派生类的引用或指针赋给基类引用或指针
保护继承是私有继承的变体。保护继承在列出基类时使用关键字 protected:
,各继承方式的权限转换如下
特征
公有继承
保护继承
私有继承
公有成员变成
派生类的公有成员
派生类的保护成员
派生类的私有成员
保护成员变成
派生类的保护成员
生类的保护成员
派生类的私有成员
私有成员变成
只能通过基类接口访问
只能通过基类接口访问
只能通过基类接口访问
能否隐式向上转换
是
是(但只能在派生类中)
否
类模板
C++的类模板为生成通用的类声明提供方法
比如我们有一个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 typedef unsigned long Item;class Stack { private : enum {MAX = 10 }; Item items[MAX]; int top; public : Stack(); bool isempty () ; bool isfull () ; bool push (const Item & item) ; bool pop (Tiem & item) ; }; #endif
采用模板时,将使用模板定义替换Stack
声明,使用模板成员函数替换Stack
的成员函数。和模板函数一样,模板类以下面这样的代码开头:
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 61 #ifndef STACKTP_H_ #define STACKTP_H_ template <class Type >class Stack { private : enum {MAX = 10 }; Type items[MAX]; int top; public : Stack(); bool isempty () ; bool isfull () ; bool push (const Type & item) ; bool pop (Type & item) ; }; template <class Type >Stack <Type>: :Stack(){ top = 0 ; } template <class Type >bool Stack <Type>: :isempty(){ return top == 0 ; } template <class Type >bool Stack <Type>: :isfull(){ return top == MAX; } template <class Type >bool Stack <Type>: :push(const Type & item){ if (top < MAX) { items[top++] = item; return true ; } else return false ; } template <class Type >bool Stack <Type>: :pop(Type & item){ if (top > 0 ) { item = items[--top]; return true ; } else return false ; } #endif
使用模板类则需要实例化
1 2 Stack<int > kernels; Stack<string > colonels;
使用模板时主要注意,注意指针可能会失败,因为仅仅创建指针但是并没有分配内存
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #ifndef STCKTP1_H_ #define STCKTP1_H_ template <class Type >class Stack { private : enum {SIZE = 10 }; int stacksize; Type * items; int top; public : explicit Stack (int ss = SIZE) ; Stack(const Stack & st); ~Stack() { delete [] items; } bool isempty () { return top == 0 ; } bool isfull () { return top == stacksize; } bool push (const Type & item) ; bool pop (Type & item) ; Stack & operator =(const Stack & st); }; template <class Type >Stack <Type>: :Stack(int ss) : stacksize(ss), top(0 ) { items = new Type [stacksize]; } template <class Type >Stack <Type>: :Stack(const Stack & st) { stacksize = st.stacksize; top = st.top; items = new Type [stacksize]; for (int i = 0 ; i < top; i++) items[i] = st.items[i]; } template <class Type >bool Stack <Type>: :push(const Type & item){ if (top < stacksize) { items[top++] = item; return true ; } else return false ; } template <class Type >bool Stack <Type>: :pop(Type & item){ if (top > 0 ) { item = items[--top]; return true ; } else return false ; } template <class Type >Stack <Type> & Stack <Type>: :operator =(const Stack<Type> & st){ if (this == &st) return *this ; delete [] items; stacksize = st.stacksize; top = st.top; items = new Type [stacksize]; for (int i = 0 ; i < top; i++) items[i] = st.items[i]; return *this ; } #endif
使用逻辑如下
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 #include <iostream> #include <cstdlib> // for rand(), srand() #include <ctime> // for time() #include "stcktp1.h" using namespace std ;const int Num = 10 ;int main () { srand(time(0 )); cout << "Please enter stack size: " ; int stacksize; cin >> stacksize; Stack<const char *> st (stacksize) ; const char * in[Num] = { " 1: Hank Gilgamesh" , " 2: Kiki Ishtar" , " 3: Betty Rocker" , " 4: Ian Flagranti" , " 5: Wolfgang Kibble" , " 6: Portia Koop" , " 7: Joy Almondo" , " 8: Xaverie Paprika" , " 9: Juan Moore" , "10: Misha Mache" }; const char * out[Num]; int processed = 0 ; int nextin = 0 ; while (processed < Num) { if (st.isempty()) st.push(in[nextin++]); else if (st.isfull()) st.pop(out[processed++]); else if (rand() % 2 && nextin < Num) st.push(in[nextin++]); else st.pop(out[processed++]); } for (int i = 0 ; i < Num; i++) cout << out[i] << endl ; cout << "Bye\n" ; return 0 ; }
参考链接
《C++ Primer Plus》