1. 程式人生 > >C++(例題集—簡單解析-多型)

C++(例題集—簡單解析-多型)

運算子過載

例題7-1

#include<iostream>
using namespace std;
class Complex{
    double real;
    double imag;
public:
    Complex(double r1=0,double img=0):real(r1),imag(img){}
    friend Complex add(const Complex &left,const Complex &right);
    void show(){
        cout<<real<<","<<imag<<endl;
    }
};
Complex add(const Complex <,const Complex& rt){
    return Complex(lt.real+rt.real,lt.imag+rt.imag);
}
int main(){
    Complex c1(1,2),c2(3,4),c;
    //c=c1+c2;
    c=add(c1,c2);
    c.show();
    return 0;
}

例題7-2

#include<iostream>
using namespace std;
class Complex{
    double real;
    double imag;
public:
    Complex(double r=0,double i=0):real(r),imag(i){}
    friend Complex operator +(const Complex &L,const Complex &R);
    void show(){
        cout<<real<<","<<imag<<endl;
    }
};
Complex operator +(const Complex &L,const Complex &R){
    return Complex(L.real+R.real,L.imag+R.imag);
}
int main()
{
    Complex c1(1,2),c2(3,4),c;
    c=c1+c2;
    c.show();
    return 0;
}

例題:過載+

#include<iostream>
using namespace std;
class Complex{
    double real;
    double imag;
public:
    Complex(double r=0,double i=0):real(r),imag(i){}
    Complex operator +(const Complex &r);
    void show(){
        cout<<"("<<real<<","<<imag<<")\n";
    }
};
Complex Complex::operator +(const Complex &r){
    return Complex(real+r.real,imag+r.imag);
}
int main()
{
    Complex c1(1,2),c2(3,4),c;
    c=c1+c2;
    c.show();
    return 0;
}

例題:過載複數==:

#include<iostream>
using namespace std;
class Complex{
    double real;
    double imag;
public:
    Complex(double r1=0,double img=0):real(r1),imag(img){}
    friend bool operator ==(const Complex &L,const Complex &R);
};
bool operator==(const Complex &L,const Complex &R){
    return (L.real==R.real&&L.imag==R.imag);
}
int main()
{
    Complex c1(1,2),c2(3,4),c3(1,2);
    if(c1==c2){
        cout<<"C1==C2"<<endl;
    }else{
        cout<<"C1!=C2"<<endl;
    }
    if(c1==c3){
        cout<<"C1==C3"<<endl;
    }else{
        cout<<"C1!=C3"<<endl;
    }
    return 0;
}

把上面的過載變成    成員函式:

#include<iostream>
using namespace std;
class Complex{
    double real;
    double imag;
public:
    Complex(double r1=0,double img=0):real(r1),imag(img){}
    bool operator ==(const Complex &R);
};
bool Complex :: operator==(const Complex &R){
    return (real==R.real&&imag==R.imag);
}
int main()
{
    Complex c1(1,2),c2(3,4),c3(1,2);
    if(c1==c2){
        cout<<"C1==C2"<<endl;
    }else{
        cout<<"C1!=C2"<<endl;
    }
    if(c1==c3){
        cout<<"C1==C3"<<endl;
    }else{
        cout<<"C1!=C3"<<endl;
    }
    return 0;
}

過載運算子<<為日期類Date的友元函式,輸出日期.

#include<iostream>
#include<cstring>
using namespace std;
class Date{
    int Y,M,D;
public:
    Date(int ye,int mn,int day):Y(ye),M(mn),D(day){}
    friend ostream & operator <<(ostream &out,Date &date);
};
ostream &operator<<(ostream &out,Date &date){
   out<<date.Y<<" - "<<date.M<<" - "<<date.D<<endl;
   return out;
}
int main()
{
    Date d1(2014,01,25),d2(2017,02,35);
    cout<<d1<<d2;
    return 0;
}

過載運算子:++前置,++後置,<<流運算.

#include<iostream>
#include<cstring>
using namespace std;
class Time{
    int H,M,S;
public:
    Time (int h=0,int m=0,int s=0):H(h),M(m),S(s){}
    Time &operator++();
    Time operator++(int);
    friend ostream &operator <<(ostream &out,Time &time);
};
Time &Time::operator++(){
    S=(S+1)%60;
    if(S==0){
        if(M==59)
            H=(H+1)%24;
        M=(M+1)%60;
    }
    return (*this);
}
Time Time::operator++(int){
    Time old=*this;
    S=(S+1)%60;
    if(S==0){
        if(M==59)
            H=(H+1)%24;
        M=(M+1)%60;
    }
    return old;
}
ostream& operator<<(ostream &out,Time &time){
    out<<time.H<<":"<<time.M<<":"<<time.S;
    return out;
}
int main()
{
    Time t(11,59,58);
    for(int i=0;i<4;i++){
           t++; cout<<t<<endl;         
          //cout<<t++<<endl;
    }
    cout<<endl;
    for(int i=0;i<3;i++){
        cout<<++t<<endl;
    }
    return 0;
}

子類與基類的相容性

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
class Student{
    string name;
    int age;
    double score;
public:
    Student (string nm,int age,double score);
    void display()const ;
};
Student::Student(string nm,int a,double s):name(nm),age(a),score(s){};
void Student::display()const{
    cout<<"姓名:"<<name<<",年齡:"<<age<<",分數"<<score<<endl;
}
class Graduate:public Student{
    string sy;
public:
    Graduate(string name,int age,double score,string ss);
    void display()const;
};
Graduate::Graduate(string nm,int a,double s,string ss):Student(nm,a,s),sy(ss){};
void Graduate::display() const{
    Student::display();
    cout<<"專業:"<<sy<<endl;
}
int main()
{
    Student st("李強",18,95);
    Graduate gt("高原",25,93,"軟體工程");
    st.display();
    gt.display();
    st=gt;
    st.display();
    Student &rs=gt;
    rs.display();
    Student *ps;
    ps=>
    ps->display();
    return 0;
}

子類物件呼叫父類成員函式:

#include<iostream>
using namespace std;
class Bird{
public:
    void singing(){
        cout<<"bird singing...."<<endl;
    }
};
class Sparrow:public Bird{
public:
    void singing(){
        cout<<"Sparrow jiji zha....."<<endl;
    }
};
class Crow:public Bird{
public:
    void singing(){
        cout<<"Crow GuGuGuGuGu....."<<endl;
    }
};
int main()
{
    Bird bird;
    Sparrow sparrow;
    Crow crow;
    bird.singing();
    sparrow.singing();
    crow.singing();
    sparrow.Bird::singing();
    return  0;
}

例題7-7 用迴圈來實現例題7-6(反例)

大家通過執行就明白了什麼回事了,因為這個它只有父類指標,指著只有父類的位置來展示。

所以執行一下程式碼就變成全部是  Bird singing....

#include<iostream>
using namespace std;
class Bird{
public:
    void singing(){
        cout<<"bird singing...."<<endl;
    }
};
class Sparrow:public Bird{
public:
    void singing(){
        cout<<"Sparrow jiji zha....."<<endl;
    }
};
class Crow:public Bird{
public:
    void singing(){
        cout<<"Crow GuGuGuGuGu....."<<endl;
    }
};
void sing(Bird *bird){
    bird->singing();
}
int main()
{
    Bird bird;
    Sparrow sparrow;
    Crow crow;
    Bird *p[]={&bird,&sparrow,&crow};
    //bird.singing();
    //sparrow.singing();
    //crow.singing();
    //sparrow.Bird::singing();
    for(int i=0;i<3;i++){
        sing(p[i]);
    }
    return  0;
}

通過上面的示例缺少了個體的特性,所以引入了虛擬函式的概念。

虛擬函式的作用

引例:

#include<iostream>
using namespace std;
class Base{
public:
      virtual void show(){
            cout<<"showing base.\n";
      }
};
class D:public Base{
public:
    virtual void show(){
        cout<<"showing D.\n";
    }
};
int main()
{
    Base base;
    D d;
    
    Base *p=&d;
    p->show();              // 輸出“  D   ”
    
    Base &r=d;              // 輸出“  D   ”同上,上面用的是指標訪問,而這個是引用來實現訪問。
    r.show();
    
    base=d;                 //但是子類物件賦值基類物件後,基類物件呼叫的仍然是基類的虛擬函式.
    base.show();            //輸出“   Base    ”
    
    return 0;
}

例題7-9        虛擬函式的多型性

利用了虛擬函式來改善了例題7-7的弊端。

#include<iostream>
using namespace std;
class Bird{
public:
    virtual void singing(){
        cout<<"bird singing.....\n";
    }
};
class Sparrow:public Bird{
public:
    virtual void singing(){
        cout<<"sparrow jijizha ....\n";
    }
};
class Crow:public Bird{
public:
    virtual void singing(){
        cout<<"Crow GuGuGuGuGuGu ....\n";
    }
};
void sing(Bird *bird){
    bird->singing();
}
void sing(Bird &rb){
    rb.singing();
}
void sing2(Bird bird){
    bird.singing();
}

int main(){
    Bird bird;
    Sparrow sparrow;
    Crow crow;
    
    sing2(sparrow);         //輸出“Bird jijizha ..."  
                            // 和例題7-6犯了一樣的錯誤。只能輸出父類中的singing()
    
    sing(sparrow);          //輸出“sparrow jijizha ..."
    
    Bird *p[]={&bird,&sparrow ,&crow};
    for(int i=0;i<3;i++){  
        sing(p[i]);    //依次輸出“Bird/sparrow/Crow jijizha ..."
    }
    return 0;
}

例題7-10 人Person派生學生類,學生類派生研究生類Graduate;

#include<iostream>
#include<cstring>
using namespace std;
class Person{
    char *name;
    bool isMale;
    int age;
public:
    Person(char *name,bool isMale, int initage);
    ~Person(){
        delete []name;
    }
    virtual void show()const;
};
Person::Person(char *nm,bool isM,int a):isMale(isM),age(a){
    name=new char[strlen(nm)+10];
    strcpy(name,nm);
}
void Person::show()const{
    cout<<name<<",";
    if(isMale)cout<<"男";
    else cout<<"女";
    cout<<","<<age<<"歲";
}
class Student :public Person{
    double score;
public:
    Student(char *name,bool isMale,int initAge,double initScore);
    virtual void show()const;
};
Student::Student(char *nm,bool ism,int a,double s):Person(nm,ism,a),score(s){}
void Student::show() const{
    Person::show();
    cout<<"分數:"<<score;
}
class Graduate:public Student{
    char sy[20];
public:
    Graduate(char *name,bool isMale,int initage,double initScore,char spy[]);
    void show()const;
};
Graduate::Graduate(char *nm,bool ism,int a,double s,char spy[]):Student(nm,ism,a,s){
    strcpy(sy,spy);
}
void Graduate::show() const{
    Student::show();
    cout<<",專業:"<<sy<<endl;
}
void fun(Person *person){
    person->show();
}
int main ()
{
    Person psn("高峰",true,20);
    Student st("李紅",false,18,95);
    Graduate gt("王濤",true,25,95,"電子學");
    Person *p[]={&psn,&st,>};
    for(int i=0;i<3;i++){
        fun(p[i]);
        cout<<endl;
    }
        return 0;
}

解析:基類Person中宣告虛擬函式Show(),子類Student和Graduate中的同名函式自動成為虛擬函式,定義

一個fun()函式,函式形參為基類指標,函式的作用是呼叫show()虛擬函式.當基類指標指向子類時,

因呼叫的是虛擬函式,所以子類顯示了學生和研究生各自的特性.

        當類族中某個成員函式定義為虛擬函式時,物件與函式的繫結是在執行時實施的.雖然這種後繫結能夠

實現執行時的多型效應,但這種繫結會增加程式執行的開銷,且有若干限制條件.

(    1    )、靜態成員函式不能宣告為虛擬函式,因為靜態成員屬於類,不專屬於某個物件。

(    2    )、行內函數不能宣告為虛擬函式,因為行內函數在編譯是已被明確的執行程式碼替換。

(    3    )、建構函式不能是虛擬函式。建構函式進行物件初始化是,物件的狀態尚未完全確定。

例題7-11 虛解構函式

#include<iostream>
using namespace std;
class Base{
public:
    ~Base(){    //virtual ~Base() 目前沒有把解構函式定義為虛擬函式.
        cout<<"呼叫了  Base  解構函式"<<endl;
    }
};
class D:public Base{
    int *p;
public:
    D(){
        p=new int(5);
    }
    ~D(){
        cout<<"呼叫了 D 的解構函式"<<endl;
        delete p;
    }
};
void fun(Base *pb){
    delete pb;
}
int main()
{
    Base *b=new D();        
    fun(b);
    // 若沒定義虛擬函式則輸出 “呼叫了Base 解構函式”;
    // 若定義了則輸出      “呼叫了 D  解構函式”
    //                  “呼叫了Base解構函式”
    return 0;
}

結果顯示:

在基類的解構函式沒有宣告為虛擬函式時,派生類的解構函式沒有呼叫,派生類物件動態開闢的記憶體

沒有釋放,造成了記憶體洩漏。

將基類的解構函式改為虛解構函式後,解構函式具有了多型效應,程式呼叫了不同的解構函式。

純虛擬函式和抽象類

抽象類例子:

#include<iostream>
#include<cstring>
using namespace std;
class P{
    char name[10];
    int age;
public:
    P(char name[],int age);
    virtual void exercise()=0;
    virtual void show();
};
class BP:public P{
    int bage;
public:
    BP(char name[],int age,int bage);
    virtual void exercise()=0;
    virtual void show();
};
class FP:public BP{
public:
    FP(char name[],int age,int bage);
    virtual void exercise();
};
class BsP:public BP{
public:
    BsP(char name[],int age,int bage);
    virtual void exercise();
};
class VP:public BP{
public:
    VP(char nm[],int age,int bage);
    virtual void exercise();
};
P::P(char nm[],int a):age(a){
    strcpy(name,nm);
}
void P::show(){
    cout<<name<<","<<age<<"歲.";
}
BP::BP(char nm[],int a,int bAge):P(nm,a),bage(bAge){}
void BP::show(){
    P::show();
    cout<<"球齡:"<<bage<<"年 .";
}
FP::FP(char nm[],int a,int bAge):BP(nm,a,bAge){}
void FP::exercise(){
    cout<<" 踢足球.\n";
}
BsP::BsP(char nm[],int a,int bAge):BP(nm,a,bAge){}
void BsP::exercise(){
    cout<<" 打籃球.\n" ;
}
VP::VP(char nm[],int a,int bAge):BP(nm,a,bAge){}
void VP::exercise(){
    cout<<" 打排球.\n" ;
}
void play(P& player){
    player.show();
    player.exercise();
}
int main(){
    FP fplay("高峰",20,8);
    BsP bplay("王強",19,7);
    VP vplay("張麗",18,6);
    play(fplay);
    play(bplay);
    play(vplay);
    return 0;
}

程式中基類運動員"練習"exercise()是抽象的運動,無法具體實現,所以函式exercise()宣告為類Player的純虛擬函式,

因此類player為抽象基類.抽象類P 不能例項化,只有子類球員BP才能具體說明球齡,但函式exercise()仍無法具體實現,

因此類BP依然為抽象類。直到子類足球運動員,籃球運動員和排球運動員是,父類的純虛擬函式exercise()才能具體實現,

才可以建立各自的物件,展示各自的行為

例題7-13 將形狀Shape設計成抽象類,實現執行時的多型.

#include<iostream>
using namespace std;
class Shape{
public:
    virtual char *getName()=0;
    virtual double getArea()=0;
};
class T:public Shape{
    double width,height;
public:
    T(double w,double h):width(w),height(h){}
    char *getName(){
        return "三角形";
    }
    double getArea(){
        return (0.5*width*height);
    }
};
class R:public Shape{
    double width,length;
public:
    R(double wid,double len):width(wid),length(len){}
    char *getName(){
        return "長方形";
    }
    double getArea(){
        return width*length;
    }
};
int main()
{
    Shape *ps;
    T t(10,5);
    R r(2,8);
    ps=&t;
    cout<<"形狀:"<<ps->getName()<<",面積:"<<ps->getArea()<<endl;
    ps=&r;
    cout<<"形狀:"<<ps->getName()<<",面積:"<<ps->getArea()<<endl;
    return 0;
}

模板

找到兩個數,三個數裡面的最大值

#include<iostream>
using namespace std;
template<typename T >T maxz(T x,T y);
template<typename S >S maxz(S x,S y,S z);
int main()
{
    cout<<maxz(2,5)<<endl;
    cout<<maxz(2.9,6.2)<<endl;
    cout<<maxz(1,2,3)<<endl;
    return 0;
}
template<typename T >T maxz(T x,T y){
    return x>y?x:y;
}
template<typename S >S maxz(S x,S y,S z){
    S temp=maxz(x,y);
    return temp>z?temp:z;
}

模板類

#include<iostream>
using namespace std;
template <class T1,class T2>
class Myclass{
    T1 x;
    T2 y;
public:
    Myclass(T1 a,T2 b):x(a),y(b){}
    void show();
};
template <class T1,class T2>void Myclass<T1,T2>::show(){
    cout<<"X="<<x<<"\tY="<<y<<endl;
}
int main()
{
    Myclass<int,char>obj1(4,'w');
    Myclass<double ,char >obj2(5.8,'w');
    obj1.show();
    obj2.show();
    return 0;
}