繼承
類之間的關係
繼承:
在已有類的基礎上創建新類的過程
一個B類繼承A類,或稱從類A派生B類——A稱爲基類(父類),B稱爲派生類(子類)
基類和派生類
class 派生類名 : 基類名錶
{
數據成員和成員函數聲明
};
基類名錶 :
訪問控制 基類名1, 訪問控制 基類名2 ,… , 訪問控制 基類名n
注意:==種方式繼承基類,派生類都不能直接使用基類的私有成員 ==
派生類的生成過程經歷了三個步驟:
派生類的對象結構
公有繼承
例題
定義一個基類person(不定義構造函數)
姓名、性別、年齡(訪問權限設置爲私有)
定義公有的成員函數set_p()
定義公有的成員函數display_p(),顯示person的信息
再由基類派生出學生類(不定義構造函數,採用公有繼承的方式)
增加學號、班級、專業和入學成績
定義公有成員函數set_t()
定義成員函定義公有的成員函數display_s(),顯示所有的信息
#include<iostream>
#include <string>
using namespace std;
class Person
{
string name;
int age;
string sex;
public:
void set_p()
{
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}
void show_p()
{
cout<<name<<" "<<age<<" "<<sex<<endl;
}
};
方法一:
class student :public Person
{
string no;
string zhuanye;
string t_class;
float score;
public:
void set_t()
{
set_p(); //調用繼承於基類的成員函數訪問繼承於基類的私有數據成員
cout<<"zhuanye\tt_class\tscore"<<endl;
//cin>>zhuanye>>t_class>>score;//錯誤,沒有輸入基類繼承過來的成員
cin>>name>>age>>sex>>zhuanye>>t_class>>score;
}
void show_t()
{
show_p();
//cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;//沒有顯示學號
cout<<name<<" "<<age<<" "<<sex<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
方法二:
class student :public Person
{
string no;
string zhuanye;
string t_class;
float score; //設計自己成員
public:
//改造基類成員 成員函數的覆蓋
void set()
{ //隱藏了基類中的同名成員
Person::set(); //調用繼承於基類的成員函數訪問繼承於基類的數據成員(首先調用基類函數)
cout<<"zhuanye\tt_class\tscore"<<endl;
cin>>zhuanye>>t_class>>score;
}
void show()
{
Person::show();//不但顯示自己成員,還顯示基類成員 基類成員的使用
cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
重名成員
派生類中訪問靜態成員
基類初始化
例題
調用構造函數順序測試,構造函數無參數
#include<iostream>
using namespace std ;
class Base
{
public :
Base ( ) { cout << "Base created.\n" ; }
} ;
class D_class : public Base
{
//派生類構造函數
public :
D_class ( ) { cout << "D_class created.\n" ; }
} ;
int main ( )
{
D_class d1 ;//先調用基類構造函數再本身
}
派生類構造函數
==派生類構造函數和析構函數的定義規則 ==
派生類構造函數和析構函數的使用原則
- 類的構造函數和析構函數不能被繼承
- 如果基類沒有定義構造函數或有無參的構造函數, 派生類也可以不用定義構造函數
- 如果基類無無參的構造函數,派生類必須定義構造函數
- 如果派生類的基類也是派生類,則每個派生類只負責直接基類的構造
- 派生類是否定義析構函數與所屬的基類無關
調用基類構造函數對基類成員進行初始化。
派生類::派生類名(參數總表):基類名(參數表)
{
// 派生類新增成員的初始化語句
}
派生類析構函數
繼承的應用實例
class Point
{ friend ostream &operator<< (ostream &, const Point &);
public:
Point( int = 0, int = 0 ) ; // 帶默認參數的構造函數
void setPoint( int, int ) ; // 對點座標數據賦值
int getX() const { return x ; } int getY() const { return y ; }
protected: int x, y; // Point類的數據成員
};
class Circle : public Point
{ friend ostream &operator<< (ostream &, const Circle &); // 友元函數
public:
Circle(double r=0.0, int x=0, int y=0); // 構造函數
void setRadius(double); /*置半徑*/ double getRadius() const; /*返回半徑*/
double area() const; // 返回面積
protected: double radius; // 數據成員,半徑
};
class Cylinder:public Circle
{ friend ostream & operator<<(ostream &, const Cylinder &); // 友元函數
public:
Cylinder(double h=0.0, double r=0.0, int x=0, int y=0); // 構造函數
void setHeight(double); /* 置高度值*/ double getHeight() const; /* 返回高度值*/
double area() const; /* 返回面積*/ double volume() const; /* 返回體積*/
protected: double height; // 數據成員,高度
};
多繼承
class 派生類名 : 訪問控制 基類名1 , 訪問控制 基類名2 , … , 訪問控制 基類名n
{
數據成員和成員函數聲明
};
多繼承的構造函數
派生類名(參數總表):基類名1(參數表1),基類名2(參數表2),…,基類名n(參數表n)
{
// 派生類新增成員的初始化語句
}
多繼承方式下構造函數的執行順序:
虛基類
如果一個派生類從多個基類派生,而這些基類又有一個共同的基類,則在對該基類中聲明的名字進行訪問時,可能產生二義性。
賦值兼容原則
賦值兼容規則指在程序中需要使用基類對象的任何地方,都可以用公有派生類的對象來替代。
虛函數和多態
多態性(Polymorphism)是指一個名字,多種語義;或界面相同,多種實現。
靜態聯編
聯編是指一個程序模塊、代碼之間互相關聯的過程。
靜態聯編,是程序的匹配、連接在編譯階段實現,也稱爲早期匹配。
重載函數使用靜態聯編
類指針的關係
基類指針和派生類指針與基類對象和派生類對象4種可能匹配:
直接用基類指針引用基類對象;
直接用派生類指針引用派生類對象;
用基類指針引用一個派生類對象;
用派生類指針引用一個基類對象。
虛函數和動態編聯
冠以關鍵字 virtual 的成員函數稱爲虛函數
注意實現運行時多態的關鍵首先是要說明虛函數,另外,必須用
基類指針調用派生類的不同實現版本
基類指針雖然獲取派生類對象地址,卻只能訪問派生類從基類繼承的成員
#include<iostream>
using namespace std ;
class Base
{ public : Base(char xx) { x = xx; }
void who() { cout << "Base class: " << x << "\n" ; }
protected: char x;
} ;
class First_d : public Base
{ public : First_d(char xx, char yy):Base(xx) { y = yy; }
void who() { cout << "First derived class: "<< x << ", " << y << "\n" ; }
protected: char y;
} ;
class Second_d : public First_d
{ public :
Second_d( char xx, char yy, char zz ) : First_d( xx, yy ) { z = zz; }
void who() { cout << "Second derived class: "<< x << ", " << y << ", " << z << "\n" ; }
protected: char z;
} ;
int main()
{ Base B_obj( 'A' ) ; First_d F_obj( 'T', 'O' ) ; Second_d S_obj( 'E', 'N', 'D' ) ;
Base * p ;
p = & B_obj ; p -> who() ;
p = &F_obj ; p -> who() ;
p = &S_obj ; p -> who() ;
F_obj.who() ;
( ( Second_d * ) p ) -> who() ;
}
運行結果:
注意
一個虛函數,在派生類層界面相同的重載函數都保持虛特性
虛函數必須是類的成員函數
不能將友元說明爲虛函數,但虛函數可以是另一個類的友元
析構函數可以是虛函數,但構造函數不能是虛函數
虛函數的重載
虛析構函數
例題
成員函數調用析構函數(採用動態聯編):
class Animal
{
string name;
public:
Animal(string a_name):name(a_name){}
virtual void show(){} //將來被派生類繼承
void show_name()
{
cout<< "The name is "<<name<<".“<<endl;
}
};
class Cat :public Animal
{
string kind;
public:
Cat(string a_name,string a_kind):Animal(a_name),kind(a_kind)
{}
void show(); //覆蓋函數(同名同參數)
};
void Cat::show()
{
show_name();
cout<<" It's a "<<kind<<endl;
}
class Dog:public Animal
{
string kind;
public:
Dog(string a_name,string a_kind):Animal(a_name),kind(a_kind)
{}
void show();
};
void Dog::show()
{
show_name();
cout<<" It's a "<<kind<<endl;
}
class Tiger:public Cat
{
public:
Tiger(string a_name,string a_kind):Cat(a_name,a_kind)
{}
};
nt main()
{
Animal *p; //基類指針
Cat cat("Tom","cat");
Dog dog("Jerry","Dog");
Tiger tiger("DuDu","Tiger");
p=&cat; //綁定貓對象
p->show();
p=&dog; //綁定狗對象
p->show();
p=&tiger; //綁定老虎對象
p->show();
return 0;
}
純虛函數抽象類
注意
純虛函數在派生類中必須重寫
虛函數與多態的應用
例題
class Employee
{
public:
Employee(const int,const string );
virtual ~Employee();
const string getName() const;
const int getNumber() const;
virtual double earnings() const=0;//僱員工資純虛函數
virtual void print() const;
protected:
int number; // 編號
string name; // 姓名
};
class Manager : public Employee
{
public:
Manager(const int , const string, double =0.0);
~Manager() { }
void setMonthlySalary(double); //新增函數
virtual double earnings() const;
virtual void print() const;
private:
double monthlySalary ;
};
class HourlyWorker : public Employee
{
public:
HourlyWorker(const long, const string, double=0.0, int =0 );
~HourlyWorker(){}
void setWage(double);
void setHours(int);
virtual double earnings() const;
virtual void print() const;
private:
double wage;
double hours;
};
class PieceWorker : public Employee
{
public:
PieceWorker(const long , const string, double =0.0, int =0 );
~PieceWorker() { }
void setWage ( double ) ; //完成件數
void setQuantity ( int ) ; //價值
virtual double earnings() const;
virtual void print() const;
private:
double wagePerPiece;
int quantity;
};
學習總結
通過對圖書館管理系統的一步步完善,漸漸把知識運用到了實際應用中,逐步消化慢慢掌握。
作業中用到的知識就是比着抄代碼,以後應該多寫代碼,熟能生巧。
C++結課了,曾經因爲C很喜歡編程,後來因爲什麼漸漸少了熱愛。
加下來面臨的是課程設計,是一個昇華的任務,要重拾熱愛,盡所能去完成任務!