C++中的抽象基類(Abstract Base Class)

張景睿218發表於2020-11-30

背景:

有時候,我們想呼叫兩個相似類的同名函式,如下圖:

Circle類:

class Circle
{
public: 
	Circle(double radius)
		: radius{radius}
	{
	} 
	double area() const
	{
		return radius * radius * 3.1415927;
	}
private:
	double radius;
}

Rectangle類:

class Rectangle
{
public: 
	Rectangle(double width, double height)
		: width{width}, height{height}
	{
	} 
	double area() const
	{
		return width * height;
	}
private:
	double width;
	double height;
}

現在,在我們的主函式中,我們想把兩個類傳到函式的引數中,問題來了,我們如何確定這個引數是什麼型別?

void printArea(??? someShape)
{
	std::cout << "The area of the shape is " << someShape.area() << std::endl;
}

我們可以用笨方法,寫兩個函式,一個使用Circle型別,一個使用Rectangle型別,就可以解決這個問題。但會不會太麻煩了?如果我們有更多的類呢?

這時候,就要用到這篇部落格的主題了:抽象基類。

抽象基類

我們可以使用一個父類,讓Circle和Rectangle類變成它的子類,從而可以override相同的函式。而在printArea函式中,我們只要傳入這個父類就可以了。

我們可以在父類裡寫一個隨便返回任何值的area函式,然後在每個子類中具體實現area的功能。

class Shape
{
public:
	virtual double area() const
	{
		return -1.0;
	}
}

現在,我們可以在Circle和Rectangle檔案中繼承Shape類,加入override關鍵字,從而在printArea函式中就可以傳入Shape型別了。

void printArea(const Shape& someShape)
{
	std::cout << "The area of the shape is " << someShape.area() << std::endl;
}

問題:

現在,我們已經可以得到我們想要的了,不過有一點小問題:

void foo(Circle c)
{
}
foo(5.0);

如果我們執行這段程式碼,計算機不會報錯,相反,它會把5.0當成半徑。這種情況只會出現在類只有一個引數的時候。這種情況在C++中叫做 隱式型別轉換 (implicit type conversion)。

我們可以用關鍵字explicit解決這個問題

class Circle
{
public: 
	explicit Circle(double radius)
		: radius{radius}
	{
	} 
	double area() const
	{
		return radius * radius * 3.1415927;
	}
private:
	double radius;
}

還有一個小問題是關於父類的。返回一個隨意值不是一個很簡潔的方法,因為我們完全不需要這個函式去實現任何東西。

class Shape
{
public:
	virtual double area() const = 0;
}

這裡讓最後 = 0,使這個函式變成了純虛擬函式,僅用於抽象基類的子類去重新定義此函式。

========================================

以上是C++抽象基類的簡單介紹

//此部落格用於自我學習的記錄,若某處不當請指正:)

相關文章