2012-03-23 153 views
3

我明白了爲什麼基類指針指向派生類對象。但是,我不明白爲什麼我們需要爲它分配一個基類對象,而它本身就是一個基類對象。虛函數 - 基類指針

任何人都可以解釋一下嗎?

#include <iostream> 
using namespace std; 
class base { 
    public: 
     virtual void vfunc() { 
      cout << "This is base's vfunc().\n"; 
     } 
}; 
class derived1 : public base { 
     public: 
      void vfunc() { 
       cout << "This is derived1's vfunc().\n"; 
    } 
}; 
int main() 
{ 
    base *p, b; 
    derived1 d1; 
    // point to base 
    p = &b; 
    p->vfunc(); // access base's vfunc() 
    // point to derived1 
    p = &d1; 
    p->vfunc(); // access derived1's vfunc() 
    return 0; 
} 
+3

你是什麼意思:**爲什麼我們需要分配給它,基類對象**你可以更清楚一點。 – 2012-03-23 05:26:27

+0

@Rohit:在上面的例子中,當調用成員函數時,爲什麼當p本身是一個基類指針時,語句p =&b,爲什麼不直接使用它,如:p-> func()。我知道,我錯了,但我需要糾正它。 – user980411 2012-03-23 05:29:40

回答

3

因爲指針本身不能做任何事情。
指針必須指向一個有效的對象,以便您可以使用它。

爲什麼上述說法?

一步一步的解釋可能會清除你的疑問。

步驟1:

base *p; 

創建一個指針p能夠存儲base類的對象的地址。但它沒有初始化,它指向內存中的任何隨機地址。

步驟2:

p = &b; 

一個有效base對象的地址分配給指針pp現在包含此對象的地址。

步驟3:

p->vfunc(); // access base's vfunc() 

Dererences指針p和調用由它指向的對象上的方法vfunc()。即:b

如果刪除第2步:,你的代碼只是試圖取消引用一個未初始化的指針,並會導致Undefined Behavior &最有可能崩潰。

+0

我似乎理解**的關鍵**。你能否多解釋一下。 – user980411 2012-03-23 05:28:11

+0

謝謝。這有幫助。 – user980411 2012-03-23 05:32:11

+1

@ user980411:我對它進行了更新,使其更具說明性。希望有幫助。但實際上你需要選擇一個[良好的C++書](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list)。 – 2012-03-23 05:37:07

1

不強制調用

p = &b; 
p->vFunc(); 

可以直接調用

b.vFunc(); 

都將給你相同的結果。

不過看起來你應該明白虛擬功能的威力。假設您想要存儲basederived1對象的10個實例並重復調用該函數,那麼您將如何執行該操作?或者如果你想將它傳遞給一個通用函數,將它存儲在數組中之後?

vase *p[4]; 

base b1; 
derived d; 

p[0] = new base(); 

p[1] = &b1; 

p[2] = new dervied1();  

p[3] = &d; 

for (int i =0 ;i <4 ;i++) 
{ 
    p[i]->vFunc(); 
} 
+0

謝謝。行** p [0] = new base(); ** return? – user980411 2012-03-23 05:41:01

+1

@ user980411它會給你基類對象的指針 – Jeeva 2012-03-23 05:42:00

+0

指針在這個意義上,會** new base()**創建一個類型爲base的對象,並返回它的地址給p [0] ... ??? – user980411 2012-03-23 05:45:37

1

我不完全知道我理解你的問題很好,但典型情況下使用虛擬函數的是,當我們編寫一個函數,並希望它與基地的目標努力類或任何從它派生:

struct base { 
    virtual void dosomething() = 0; 
}; 

void myfunc(base *b) { 
    b->dosomething(); 
} 

當我們寫myfunc,我們既不知道也不關心所涉及的對象的確切身份 - 我們只關心它知道事實如何dosomething的命令。

就像你所顯示的編寫代碼,我們直接將基類或派生類的對象的地址賦給一個指針,那更像是規則的例外。你說得很對,在這種情況下,使用指向base的指針來引用派生對象並沒有真正獲益。主要好處是類似於一個函數,當我們編寫代碼時派生類可能還不存在,但只要派生類符合規定的接口,它就可以工作。

+0

感謝您的真誠努力。 – user980411 2012-03-23 05:44:45

-2

這種類型的引用在應用設計模式時非常適用(您可能需要通過面向對象設計的高級課程,或者從閱讀書籍開始:首先,設計模式,我建議)

請參閱例如如何在上述書中實現java中的裝飾器模式。

public abstract class Beverage { 
String description = "Unknown Beverage"; 
public String getDescription() { 
    return description; 
} 
public abstract double cost(); 
} 

則認爲我們有咖啡和在DarkRoast兩個子類:

public class Espresso extends Beverage { 

public Espresso() { 
    this.description = "Espresso"; 
} 

public double cost() { 
    return 1.99; 
} 
} 

public class DarkRoast extends Beverage { 
public DarkRoast() { 
    description = "Dark Roast Coffee"; 
} 

public double cost() { 
    return .99; 
} 
} 
現在

,我們要添加裝飾:

public abstract class CondimentDecorator extends Beverage { 
public abstract String getDescription(); 
} 

,並建立了一些具體的裝飾:

public class Mocha extends CondimentDecorator { 

Beverage beverage; 

public Mocha(Beverage beverage) { 
    this.beverage = beverage; 
} 

public String getDescription() { 
    return beverage.getDescription() + ", Mocha"; 
} 

public double cost() { 

    return .20 + beverage.cost(); 
} 
} 

和另一包裝:

public class Whip extends CondimentDecorator { 
Beverage beverage; 

public Whip(Beverage beverage) { 
    this.beverage = beverage; 
} 

public String getDescription() { 
    return beverage.getDescription() + ", Whip"; 
} 

public double cost() { 
    return .10 + beverage.cost(); 
} 
} 

終於,看到在主函數中發生了什麼,以及我們如何帶指針的優勢,以父類:

public static void main(String[] args) { 
    Beverage beverage = new Espresso(); 
    System.out.println(beverage.getDescription() + " $" + beverage.cost()); 
    Beverage beverage2 = new DarkRoast(); 
    beverage2 = new Mocha(beverage2); 
    beverage2 = new Mocha(beverage2); 
    beverage2 = new Whip(beverage2); 
    System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); 

你能猜到的輸出是什麼?以及:

Espresso $1.99 
Dark Roast Coffee, Mocha, Mocha, Whip $1.49 
+1

你爲什麼要用java代碼回答一個C++問題? – 2012-10-20 20:24:14