1. 程式人生 > 實用技巧 >【C++】類-多型

【C++】類-多型

類-多型

[TOC]

1. 基本概念

多型性:

  • 操作介面具有表現多種形態的能力,能根據操作環境的不同採用不同的處理方式。
  • 一組具有相同基本語義的方法能在同一介面下為不同的物件服務。

實現方式:

繫結機制 繫結是將一個識別符號名和一個儲存地址聯絡在一起的過程。例如:虛擬函式的實現對應著虛表,每一個派生類都包含了一個指向虛表的指標,執行時根據物件的指標找到虛表指標,然後取出對應的函式

  • 靜態繫結:編譯階段完成
  • 動態繫結:執行時完成

分類:

  • 靜態多型性:運算子過載
  • 動態多型性:虛擬函式

2. 運算子過載

2.1 過載為類的成員函式

函式型別 operator 運算子(形參)
{
......
}
引數個數=原運算元個數-1 (後置++、--除外)

eg1:複數過載加法

class Complex {
public:
	Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
	//運算子+過載成員函式
	Complex operator + (const Complex& c2) const;
	//運算子-過載成員函式
	Complex operator - (const Complex& c2) const;
	void display() const; //輸出複數
private:
	double real; //複數實部
	double imag; //複數虛部
};
Complex Complex::operator+(const Complex& c2) const {
	//建立一個臨時無名物件作為返回值
	return Complex(real + c2.real, imag + c2.imag);
}
Complex Complex::operator-(const Complex& c2) const {
	//建立一個臨時無名物件作為返回值
	return Complex(real - c2.real, imag - c2.imag);
}void Complex::display() const {
	cout << "(" << real << ")+(" << imag << ")j" << endl;
}

eg2:時鐘類過載自增。後置自增需要傳遞一個引數

class Clock {//時鐘類定義
public:
	Clock(int hour = 0, int minute = 0, int second = 0);
	void showTime() const;
	//前置單目運算子過載
	Clock& operator ++ ();
	//後置單目運算子過載
	Clock operator ++ (int);
private:
	int hour, minute, second;
};
Clock::Clock(int hour, int minute, int second) {
	if (0 <= hour && hour < 24 && 0 <= minute && minute < 60
		&& 0 <= second && second < 60) {
		this->hour = hour;
		this->minute = minute;
		this->second = second;
	}
	else
		cout << "Time error!" << endl;
}
void Clock::showTime() const { //顯示時間
	cout << hour << ":" << minute << ":" << second << endl;
}
Clock& Clock::operator ++ () {
	second++;
	if (second >= 60) {
		second -= 60; minute++;
		if (minute >= 60) {
			minute -= 60; hour = (hour + 1) % 24;
		}
	}return *this;
}
Clock Clock::operator ++ (int) {
	//注意形參表中的整型引數
	Clock old = *this;
	++(*this); //呼叫前置“++”運算子
	return old;
}

2.2 過載為非成員函式

如果資料是類的private成員,需要將函式宣告為友元函式

eg:複數過載加減法和輸出流運算子

class Complex {
public:
	Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }
	friend Complex operator+(const Complex& c1, const Complex& c2);
	friend Complex operator-(const Complex& c1, const Complex& c2);
	friend ostream& operator<<(ostream& out, const Complex& c);
private:
	double real; //複數實部
	double imag; //複數虛部
}; Complex operator+(const Complex& c1, const Complex& c2) {
	return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
Complex operator-(const Complex& c1, const Complex& c2) {
	return Complex(c1.real - c2.real, c1.imag - c2.imag);
}
ostream& operator<<(ostream& out, const Complex& c) {
	out << "(" << c.real << ", " << c.imag << ")";
	return out;
}

3. 虛擬函式

  • 通過virtual關鍵字實現
  • 虛擬函式必須是非靜態成員函式,也不能是行內函數(編一階段必須定下來的)
  • 建構函式不能是虛擬函式,解構函式可以

eg1:

class Base1 {
public:
	virtual void display() const; //虛擬函式
};
void Base1::display() const {
	cout << "Base1::display()" << endl;
}

class Base2: public Base1 {
public:
	virtual void display() const;
};
void Base2::display() const {
	cout << "Base2::display()" << endl;
}

class Derived : public Base2 {
public:
	virtual void display() const override;
};
void Derived::display() const {
	cout << "Derived::display()" << endl;
}
void fun(Base1* ptr) {
	ptr->display();
}

呼叫函式fun()時,能夠根據指標的實際型別呼叫正確的函式

eg2:虛解構函式。使用了new運算子建立物件,然後要用物件指標釋放空間

#include <iostream>
using namespace std; 
class Base {
public:
	virtual ~Base(); //不是虛擬函式
};
Base::~Base() {
	cout << "Base destructor" << endl;
}
class Derived : public Base {
public:
	virtual ~Derived(); //不是虛擬函式
};
Derived::~Derived() {
	cout << "Derived destructor" << endl;
}
void release(Base* p) {
	delete p;
}
int main() {
	Derived* pObj = new Derived();
	release(pObj);
	return 0;
}

基類和子類的解構函式都會被呼叫。如果解構函式非虛,那麼只會呼叫基類的解構函式,子類中新增加的成員可能不會被釋放,造成記憶體洩漏

4. 抽象類

定義:帶純虛擬函式的類是抽象類

純虛擬函式:沒有函式體的虛擬函式 virtual 函式型別 函式名(引數表) = 0;

抽象類作用

  • 抽象類為抽象和設計的目的而宣告
  • 將有關的資料和行為組織在一個繼承層次結構中,保證派生類具有要求的行為。
  • 對於暫時無法實現的函式,可以宣告為純虛擬函式,留給派生類去實現。

注意

  • 抽象類只能作為基類來使用。
  • 不能定義抽象類的物件。

5. overridefinal

  • override顯式指明該函式是虛擬函式,編譯器檢查在基類中如果沒有對應的函式,會報錯
  • final用來避免類被繼承,或是基類的函式被改寫