天天看點

【C++ Primer】第十四章 C++中的代碼重用

序:C++的一個主要目标是促進代碼重用,其中包含公有繼承、包含、使用私有或保護繼承

一,包含對象成員的類

       1)valarray類簡介  #include <valarray>

             作用:處理數值,支援數值中所有元素的值相加,找最大值,最小值

             用法:vallarray <int>  a;               //數組 a  size=0

                        vallarray <double> b(10);   //數組  b  size=10

                        vallarray <double> c(10,8);   //數組  c  size=8  每個元素設定為 10

                        int  s={2,3,4,5,6};  vallarray <double> d(s,3); //數組d 取s的前三個元素

      2)student 類設計

<strong><span style="font-size:18px;">#include <iostream>

#include <string>

#include <valarray>

using namespace std;

class student

{

private:

typedef valarray<double> ArrayDb;//定義了一個double數組 的别名

string name;

ArrayDb scores;//數組

ostream & arr_out(ostream &os)const;

public:

/*已經初始化的類*/

student():name("NULL Student"),scores(){};

student(const string &s):name(s),scores(){};

explicit student(int n):name("NULLy"),scores(n){}; //關閉隐式轉換 因為n為數組個數

student(const string &s,int n):name(s),scores(n){};

student(const string &s,ArrayDb a):name(s),scores(a){};//分數為數組

student(const char *str,const double *pd,int n):name(str),scores(pd,n){};

~student(){cout<<"\nstudent is over";};

double Average()const;

const string &Name()const;

double &operator[](int i);

double operator[](int i)const;

/*友元函數*/

friend istream &operator>>(istream &is,student &stu);

/*如果是成員函數,而不是友元函數的話,student>>cin 這樣調用*/

friend istream &getline(istream &is,student &stu);

friend ostream &operator<<(ostream &os,const student &stu);

};

double student::Average()const //求student 平均分

{

if(scores.size()>0)

return scores.sum()/scores.size();

else

return 0;

}

const string &student::Name()const

{

return name;

}

double &student::operator[](int i)

return scores[i];

double student::operator[](int i)const

ostream & student::arr_out(ostream &os)const //将所有結果輸出

int i;

int lim=scores.size();

if(lim>0)

{

for(i=0;i<lim;i++)

{

os<<scores[i]<<" ";

if(i%5==4)

os<<endl;

}

if(i%5!=0)

os<<endl;

}

else

os<<"empty array";

return os;

istream &operator>>(istream &is,student &stu)

is>>stu.name;

return is;

}

istream &getline(istream &is,student &stu)

getline(is,stu.name);

ostream &operator<<(ostream &os,const student &stu)

os<<"scores for "<<stu.name<<":\n";

stu.arr_out(os);

return os;

}

void set(student &s,int n)

cout<<"please enter the student's name:";

getline(cin,s);

cout<<"please enter "<<n<<"quiz scores:\n";

for(int i=0;i<n;i++)

cin>>s[i];

while(cin.get()!='\n')

continue;

int main()

student ada[3]={student(5),student(5),student(5)}; //初始化學生數組

for(i=0;i<3;++i)

set(ada[i],5); //初始化學生

cout<<"\nStudent List";

for(i=0;i<3;++i) //學生名單

cout<<ada[i].Name()<<endl;

cout<<"\nResult";

for(i=0;i<3;++i) //學生平均分

cout<<endl<<ada[i];

cout<<"avaverage: "<<ada[i].Average()<<endl;

cout<<"\nDone.\n";

return 0;

}</span></strong>

1)typedef:用來聲明自定義資料類型,配合各種原有資料類型來達到簡化程式設計的目的的類型定義關鍵字。      

typedef double double_a; //為已知類型 double起别名 double_a

typedef struct node{

char *name;

int age;

} *Node;

typedef valarray<double> ArrayDb;//定義了一個double數組 的别名

2)explicit : 關閉構造函數的隐式轉換

explicit student(int n):name("NULLy"),scores(n){};

如果 student dod("xiaotian",10);//定義對象 名字為“xiaotian" 數組有5個元素

dod=5;//重新定義dod對象 名字為空,數組有5個元素

如果沒有explicit 則調用student(5); 将5轉化為一個臨時student 對象

二,私有繼承:has-a關系

1)class student : private string, private valarray<double>

不同種類的繼承

特征 公有繼承 保護繼承 私有繼承
共有成員變成 派生類的公有成員 派生類的保護成員 派生類的私有成員
保護成員變成
私有成員變成
能否隐式向上轉換

使用using 重新定義通路權限,讓私有繼承中的方法也可以調用

class student :private string,private valarray<double>

using std::valarray::mix; //using 聲明隻使用 成員名,沒有圓括号

using std::valarray::min;

}

三,多重繼承

<strong><span style="font-size:18px;">#include <string>

#include <iostream>

class worker

string fullname;

long ID;

worker():fullname("no one"),ID(0L){};

worker(const string &s, long n):fullname(s),ID(n){};

virtual ~worker()=0;// 虛函數

virtual void set();

virtual void show() const;

} ;

class waiter:public worker //服務員

int panache;

waiter():worker(),panache(0){};

waiter(const string &s,long n,int p=0):worker(s,n),panache(p){};

waiter(const worker &wk,int p=0):worker(wk),panache(0){};

void set();

void show()const;

class singer:public worker

protected:

enum{

others,alto,constralto,soprano,bass,baritone,tenor

};

enum{Vtypes = 7};

static char *pv[Vtypes];

int voice;

singer():worker(),voice(others){};

singer(const string &s,long n,int v=others):worker(s,n),voice(others){};

singer(const worker &wk,int v=others):worker(wk),voice(others){};

void show()const;

worker::~worker(){

}//虛函數必須實作

void worker::set()

cout<<"Enter worker's name:";

getline(cin,fullname);

cout<<"Enter worker's ID: ";

cin>>ID;

while(cin.get()!='\n')//記住這裡是字元而不是字元串

continue;

}

void worker::show()const

cout<<"name:"<<fullname<<endl;

cout<<"employee id"<<ID<<endl;

//waiter method

void waiter::set()

worker::set();

cout<<"Enter waiter's panache rating :";

cin>>panache;

void waiter::show()const

cout<<"category:waiter\n";

worker::show();

cout<<"panache rating :"<<panache<<"\n";

//singer method

char *singer::pv[]={

"other","alto","constralto","soprano","bass","baritone","tenor"

void singer::set()

int i;

for(i=0;i<Vtypes;++i)

cout<<i<<":"<<pv[i]<<" ";

if(i%4== 3)

cout<<endl;

if(i%4==0)

cout<<endl;

cin>>voice;

while(cin.get()!='\n')

continue;

void singer::show()const

cout<<"category:singer\n";

cout<<"vocal range :"<<pv[voice]<<endl;

const int LIM=4;

int main()

waiter bob("bob apple",314L,5);

singer bev("beverly hills",522L,3);

waiter w_temp;

singer s_temp;

worker *pw[LIM]={&bob,&bev,&w_temp,&s_temp};

for(i=2;i<LIM;++i)

pw[i]->set();

for(i=0;i<LIM;i++)

pw[i]->show();

return 0;

</span></strong>

 1)singer類和waiter類都繼承了一個worker元件,如果建立一個類singerwaiter,

class singerwaiter :public singer,public waiter{};

則singerwaiter包含兩個worker元件,這将引起問題。例如,通常可以将派生類的對象的位址賦給基類指針但是現在回出現二義性。

singerwaiter ed;

worker *pw = &ed;//這 會出現二義性

解決辦法:

1>worker *pw=(waiter *)&ed;

worker *pw=(singer *)&ed;

2>虛基類

class singer:virtual public worker{};

class waiter:public virtual worker{};

這時 class singerwaiter:public singer,public worker;

2)C++在基類是虛拟的時候,禁止資訊通過中間類自動傳遞給基類

3)singerwaiter ss; ss.show();//引起二義性

ss.singer::show();//解決問題

四,類模闆

1)template <class Type> //template 告訴編譯器,将要定義一個模闆。class 是類型名,Type 是該變量名

2)簡單的模闆使用

#include <cctype>

template <class Type>

class stack

private:

enum{MAX=10};

Type items[MAX];

int top;

stack();

bool isempty();

bool isfull();

bool push(const Type &item);//add item to stack

bool pop(Type &item);

stack<Type>::stack()

top=0;

bool stack<Type>::isempty()

return top==0;

bool stack<Type>::isfull()

return top==MAX;

bool stack<Type>::push(const Type &item)

if(top<MAX)

items[top++] = item;

return true;

else

return false;

bool stack<Type>::pop(Type &item)

if(top>0)

item=items[--top];

stack<string> st; //????????????

char ch;

string po;

cout<<"Please enter A to add a purchase order\n"<<"P to process a PO,or Q to quit.\n";

while(cin>>ch&&std::toupper(ch)!='Q')

if(!std::isalpha(ch))

cout<<'\a';

switch(ch)

case 'A':

case 'a':cout<<"Enter a po number to add:";

cin>>po;

if(st.isfull())

cout<<"stack is already full"<<endl;

else

st.push(po);

break;

case 'p':

case 'P':if(st.isempty())

cout<<"stack already empty"<<endl;

{

st.pop(po);

cout<<"po#"<<po<<"popped\n";

break;

}

cout<<"Please enter A to add a purchase order\n"<<"P to process a PO,or Q to quit.\n";

cout<<"BYE\n";

說明:1)模闆代碼不能修改參數值,不能使用參數位址。是以不能使用n++和&n這種表達式

            2)遞歸使用模闆:Array<Array<int , 5>,10>   a;//包含10個元素的數組,每個元素是包含5個元素的數組

                                            int a[10][5];  //含有十行,每行包含5個元素

            3)使用多個參數:template <class T1, class T2>  //聲明

                                             class  pa

                                            {

                                                  T1  a;

                                                  T2  b;

                                             };

                      pa<string ,int>("tianshuai",1);

複習題:虛基類與非虛基類之間的差別

                如果兩個繼承路線有相同的祖先,則類中包含兩個祖先的拷貝,而将基類設定成虛的則可以避免這種情況。