回答
抽象類的目的是爲一組具體的子類定義一個通用協議。當定義共享代碼,抽象想法等的對象時,這非常有用。
抽象類沒有實例。抽象類必須至少有一個延遲方法(或函數)。在C++實現這一點,一個純虛成員函數被聲明,但在抽象的類沒有定義:
class MyClass {
virtual void pureVirtualFunction() = 0;
}
嘗試實例化的抽象類將總是導致編譯錯誤。
「通過在每個實際類中創建每個必需的函數,定義抽象基類提供了什麼不提供 ?」
這裏的主要想法是代碼重用和適當的跨類分區。它更有意義,在父類中定義一次函數,而不是在多個子類中再定義一遍又一遍:
class A {
void func1();
virtual void func2() = 0;
}
class B : public A {
// inherits A's func1()
virtual void func2(); // Function defined in implementation file
}
class C : public A {
// inherits A's func1()
virtual void func2(); // Function defined in implementation file
}
抽象類用於定義要實現的接口。見一些參考:
http://en.wikibooks.org/wiki/C%2B%2B_Programming/Classes/Abstract_Classes
我有一隻狗。抽象類的狗用方法樹皮。我特別的狗使一個樹皮。其他狗以不同的方式吠叫。因此,以抽象的方式定義一隻狗是有用的。
當我們想要重寫派生類中的樹皮方法時,我們實際上在每個派生類中創建了函數樹皮。那麼你能否告訴我爲什麼使用它的具體原因? –
一個狗必須執行樹皮功能的特定合同的固有類。其他方面,你不會啓動課程。假設這個子類是由第三方實現的。這個抽象機制確保了第三方實現了樹皮,並且從抽象狗的角度調用樹皮函數時可以安全地使用多態性。 – Hagai
當所有具有從AbstractClass
派生的類型的對象都需要功能時,需要抽象類AbstractClass
作爲基類,但不能在AbstractClass
本身上實現。
具有派生類Car
,Motorcycle
一個基類Vehicle
的老有點人爲OO例如,...這裏提供了一個很好的例子,假設你想一個方法move()
- 你可以實現的方式,一個Car
或Motorcycle
移動,但Vehicle
s不會以通用方式移動,因此Vehicle::move()
必須是純虛擬的,因此Vehicle
是抽象的。
爲什麼我們不在每個類中創建每個必需的功能? (C++)
您必須在每個派生類中創建標記爲abstract
的每個必需函數。
如果你的問題是,爲什麼要在抽象類中創建抽象函數?
它允許嚴格run time polymorphism。
不錯,我懂了,如果我使用抽象類,我可以將它們歸類到一個組中。我可以通過使用靜態和動態轉換與基指針來施放它們。 –
抽象類允許編譯時的協議執行。這些協議定義了成爲班級家庭的一部分意味着什麼。
另一種想法是,抽象類是你的實現類必須履行的契約。如果他們不履行合同,他們不能成爲班級家庭的一員,他們必須進行修改才能符合合同規定。所提供的合同可能會提供默認功能,但它也會將其留給子類以定義更具體或不同的功能,同時仍在合同範圍內。
對於小型項目來說,這看起來似乎不太有用,但對於大型項目,它提供了符合性和結構,因爲它通過抽象類合同提供了文檔。這使得代碼更具可維護性,並且使得子類具有相同的協議,使得使用和開發新的子類更容易。
abstract class dog
{
bark();
}
// function inside another module
dogbarking(dog obj)
{
dog.bark(); // function will call depend up on address inside the obj
}
// our class
ourclass: inherit dog
{
bark()
{
//body
}
}
main()
{
ourclass obj;
dogbarking(obj);
}
我們可以看到,dogbarking是寫在另一個模塊的功能。它只知道抽象類的狗。即使它可以調用我們課程中的函數樹皮。在main函數中,我們創建了我們的類的對象,並使用抽象類dog的引用對象傳遞到它接收到的函數dogbarking。
想象一下,你有兩種方法來顯示字符串:
DisplayDialog(string s);
PrintToConsole(string s);
而且要編寫一些代碼,可以在這兩個方法之間進行切換:
void foo(bool useDialogs) {
if (useDialogs) {
DisplayDialog("Hello, World!");
} else {
PrintToConsole("Hello, World!");
}
if (useDialogs) {
DisplayDialog("The result of 2 * 3 is ");
} else {
PrintToConsole("The result of 2 * 3 is ");
}
int i = 2 * 3;
string s = to_string(i);
if (useDialogs) {
DisplayDialog(s);
} else {
PrintToConsole(s);
}
}
此代碼是緊耦合用於顯示字符串的具體方法。添加一個額外的方法,改變方法的選擇方式等都會影響到使用它的每一段代碼。此代碼是與我們用來顯示字符串的一組方法緊密耦合的。
抽象基類是解耦的一種方式使用實現該功能的代碼中的某些功能的代碼。它通過爲所有執行任務的各種方式定義一個通用界面來實現這一點。
class AbstractStringDisplayer {
public:
virtual display(string s) = 0;
virtual ~AbstractStringDisplayer();
};
void foo(AbstractStringDisplayer *asd) {
asd->display("Hello, World!");
asd->display("The result of 2 * 3 is ");
int i = 2 * 3;
string s = to_string(i);
asd->display(s);
}
int main() {
AbstractStringDisplayer *asd = getStringDisplayerBasedOnUserPreferencesOrWhatever();
foo(asd);
}
使用,使用的抽象接口將不需要改變由AbstractStringDisplayer定義的接口,我們可以創建和,因爲我們希望用顯示的字符串儘可能多的新途徑,和代碼。
有一個抽象類,像「狗」包含「樹皮」的虛擬方法允許所有類即使比格犬的樹皮的實施方式與科利犬的方式不同,它們也繼承了狗的同樣的叫法。
如果沒有共同的抽象父(或至少有樹皮虛方法共同的父),這將會是很難做到以下幾點:
有型的矢量狗包含牧羊犬,比格犬,德國牧羊犬並讓他們每個人都吠叫。使用包含Collies,Beagles,德國牧羊犬的狗矢量,你需要做的就是讓所有的樹皮都在for循環中循環,並在每個樹皮上稱呼它們。否則,你必須有一個獨立的大象矢量,矢量的小獵犬等。
如果問題是「爲什麼讓Dog抽象爲具體的時候,有一個虛擬樹皮定義了一個可以被重寫的默認實現?」,答案是有時候這可能是可以接受的 - 但是,從從設計的角度來看,真的沒有任何一種狗不是一隻牧羊犬,一隻小獵犬或者其他一些品種或混合物,儘管它們都是狗,但實際上它們中沒有一隻是狗,但是,不是其他派生類。此外,由於狗吠聲從一個品種到另一個品種是如此不同,所以不可能有任何真正可接受的默認樹皮實施方式,這對任何體面的狗羣都是可以接受的。
我希望這可以幫助你理解目的:是的,無論如何你必須在每個子類中實現樹皮,但是通用抽象祖先可以讓你將任何子類視爲基類的成員並調用行爲可能在概念上與樹皮類似,但實際上具有非常不同的實現。
我喜歡這個答案 –
感謝您使用狗的例子。它使這個概念非常清楚地理解 – TKerr
抽象類的目的是提供一個合適的基類,其他類可以從中繼承。抽象類不能用於實例化對象,只能用作接口。嘗試實例化抽象類的對象會導致編譯錯誤。 (因爲vtable條目沒有填充我們在抽象類中提到的虛函數的內存位置)
因此,如果需要實例化ABC的子類,則必須實現每個虛函數,這意味着它支持ABC聲明的接口。未能在派生類中重寫純虛函數,然後試圖實例化該類的對象,是一個編譯錯誤。
例子:
class mobileinternet
{
public:
virtual enableinternet()=0;//defines as virtual so that each class can overwrite
};
class 2gplan : public mobileinternet
{
private:
int providelowspeedinternet(); //logic to give less speed.
public:
void enableinternet(int) {
// implement logic
}
};
//similarly
class 3gplan : public enableinternet
{
private: high speed logic (different then both of the above)
public:
/* */
}
這裏在這個例子中,你能理解。
- 1. 爲什麼我們需要Java中的抽象類?
- 2. 爲什麼我們需要抽象類而不是虛擬類?
- 3. 爲什麼我們在上課時需要抽象類?
- 4. 爲什麼我們在抽象類存在時需要接口?
- 5. 爲什麼我們有抽象類時需要接口?
- 6. 爲什麼需要抽象類?
- 7. 爲什麼需要抽象類?
- 8. 爲什麼在我們的項目中需要接口層/抽象類?
- 9. 什麼是算法中的抽象數據類型,爲什麼我們需要它們?
- 10. 爲什麼我們的C++類被強制抽象?
- 11. 爲什麼你需要知道抽象類的抽象方法是否抽象
- 12. 爲什麼我們需要包裝類
- 13. 爲什麼我們需要在C#中鎖定和對象?
- 14. 爲什麼我們不能在C++中爲抽象類創建對象?
- 15. 爲什麼我們在C#中至少需要一個類?
- 16. 爲什麼我們需要在C#中類型轉換枚舉
- 17. 從抽象類和具體類繼承有什麼區別?爲什麼我們需要後者?
- 18. 爲什麼我們在java中使用抽象類和抽象方法
- 19. 爲什麼我需要擴展抽象類中的另一個構造函數?
- 20. 爲什麼要在Java中接口,如果我們有抽象類?
- 21. 如果我們傳遞對象本身,爲什麼我們需要EventArgs? C#
- 22. 什麼是伴侶對象,爲什麼我們需要它?
- 23. 爲什麼我們需要.htaccess的worpdress?
- 24. 爲什麼我們需要javascript中的自引用對象
- 25. 爲什麼我的派生類是一個抽象類? C++
- 26. 爲什麼我們需要目標C中的協議?
- 27. C#代表,爲什麼我們需要它們?
- 28. 爲什麼我們需要分開Apply和Applicative類型的類?
- 29. 在面向對象的編程中,我們需要抽象嗎?
- 30. 爲什麼我們實現接口抽象類
問題是你*在每個類中創建函數。它由編譯器執行。 – chris