2015-06-30 83 views
-5
using namespace std; 

class Foo 
{ 
    public: 
     virtual void foo(); 
     void foo2(); 
}; 
class Bar : public Foo 
{ 
    public: 
     void foo(); 
     void foo2(); 
}; 
int main() 
{ 
    Foo* f = new Foo; 
    f->foo();   **//1** 
    f->foo2();  **//2** 
    return 0; 
} 

編譯器如何知道,1)本質上是動態的,2)是靜態的。 如何在內部調用。如何調用虛函數和非虛函數

+0

如果我誤解你的問題的意圖,它是沿的線條更「編譯器如何解決其功能調用虛函數」,那麼[讀到這裏(https://stackoverflow.com/questions/ 3103153 /重載虛函數調用分辨率) – CoryKramer

+1

你應該承擔[虛函數]看看[isocpp.org(https://isocpp.org/)常見問題解答(https://isocpp.org/維基/常見問題/虛擬函數#概述虛FNS) – NathanOliver

+0

我已經寫代碼有意如此澄清疑點, 在1)編譯器會決定使用虛擬表,它必須調用的Foo :: foo的作爲指針是基類的 &in 2)編譯器靜態調用以調用Foo :: foo2。 那麼編譯器在閱讀語句後如何區分這兩種情況。 –

回答

0

在你的例子中,foo()foo2()將來自Foo類。

int main() 
{ 
    Foo* f = new Foo; 
    f->foo();      // Foo::foo 
    f->foo2();      // Foo::foo2 
    return 0; 
} 

爲您說明virtual行爲,你需要做的派生類

int main() 
{ 
    Foo* b = new Bar; 
    b->foo();      // Bar::foo 
    b->foo2();      // Foo::foo2 
    static_cast<Bar*>(b)->foo2(); // Bar::foo2 
    return 0; 
} 

請注意,在後一種情況下的一個實例,因爲b實際上是一個Bar,它調用被覆蓋的虛擬方法foo。但由於foo2未聲明virtualbFoo,因此它將調用Foo::foo2。但是,如果我們投fBar,它會調用Bar::foo2

2

Collected from here . . .

非虛成員函數的靜態解析。也就是說,成員函數是根據指向對象的指針(或引用)的類型靜態選擇的(在編譯時)。

相反,虛擬成員函數是動態解析的(在運行時)。也就是說,成員函數根據對象的類型動態選擇(運行時),而不是指向該對象的指針/引用的類型。這稱爲「動態綁定」。大多數編譯器使用以下技術的一些變體:如果對象具有一個或多個虛擬函數,編譯器會在對象中放置一個名爲「虛擬指針」或「v指針」的隱藏指針。 「這個v指針指向一個稱爲」虛擬表「或」v表「的全局表。

純虛函數是必須在派生類中重寫並且不需要定義的函數。使用curious = 0語法將虛函數聲明爲「純」。例如:

class Base { 
public: 
    void f1();  // not virtual 
    virtual void f2(); // virtual, not pure 
    virtual void f3() = 0; // pure virtual 
}; 
Base b; // error: pure virtual f3 not overridden 

在此,鹼是一種抽象的類(因爲它有一個純虛函數),所以沒有類基地對象可以被直接創建:Base是(明確地)意味着是一個基類。例如:

class Derived : public Base { 
    // no f1: fine 
    // no f2: fine, we inherit Base::f2 
    void f3(); 
}; 
Derived d; // ok: Derived::f3 overrides Base::f3 

實施例的虛擬或非虛擬Fenction

#include <iostream> 
using namespace std; 

class Base { 
    public: 
      virtual void NameOf(); // Virtual function. 
      void InvokingClass(); // Nonvirtual function. 
      }; 

// Implement the two functions. 
void Base::NameOf() { 
    cout << "Base::NameOf\n"; 
    } 

void Base::InvokingClass() { 
    cout << "Invoked by Base\n"; 
    } 

class Derived : public Base { 
    public: 
      void NameOf(); // *Virtual function*. 
      void InvokingClass(); // *Nonvirtual function.* 
    }; 

// Implement the two functions. 
void Derived::NameOf() { 
    cout << "Derived::NameOf\n"; 
} 

void Derived::InvokingClass() { 
    cout << "Invoked by Derived\n"; 
} 

int main() { 
    // Declare an object of type Derived. 
     Derived aDerived; 

    // Declare two pointers, one of type Derived * and the other 
    // of type Base *, and initialize them to point to aDerived. 
     Derived *pDerived = &aDerived; 
     Base *pBase = &aDerived; 

    // Call the functions. 
    pBase->NameOf();   // Call virtual function. 
    pBase->InvokingClass(); // Call nonvirtual function. 
    pDerived->NameOf();  // Call virtual function. 
    pDerived->InvokingClass(); // Call nonvirtual function. 
} 
0

虛擬關鍵字告訴編譯器爲動態綁定。

要在動作中查看動態綁定,請使用Bar對象實例化Foo指針,請參閱下面的代碼。

#include <iostream> 
#include <string> 
#include <algorithm> 

using namespace std; 

class Foo 
{ 
    public: 
     virtual void foo(){std::cout<<"Foo foo"<<std::endl;}; 
     void foo2(){std::cout<<"Foo foo2"<<std::endl;}; 
}; 
class Bar : public Foo 
{ 
    public: 
     void foo(){std::cout<<"Bar foo"<<std::endl;}; 
     void foo2(){std::cout<<"Bar foo2"<<std::endl;}; 
}; 
int main() 
{ 
    Foo* f = new Bar; 
    f->foo(); 
    f->foo2(); 
    return 0; 
} 
+0

我已經寫代碼有意如此澄清疑問,在1)編譯器使用虛擬表,它必須調用的Foo :: foo的作爲指針是基類&2)編譯器靜態地決定調用的Foo :: foo2的將決定。因此,在閱讀該聲明之後,編譯器如何區分這兩種情況。 –

0

其聲明或繼承了虛函數的類有一些所謂這是用來查找哪個函數,當你調用虛函數調用虛函數表。實際上,該表包含指向類中的所有的虛函數,像這樣(僞代碼 - 這可能會或可能不會進行編譯):

class Foo { 
    void foo_impl(){std::cout<<"Foo foo"<<std::endl;} 
    struct { 
     void (*foo_ptr)(); 
    } vtable; 
    public: 
     Foo(){vtable.foo_ptr = &Foo::foo_impl;} 
     void foo(){vtable.foo_ptr();} 
     void foo2(){std::cout<<"Foo foo2"<<std::endl;} 
}; 
class Bar : public Foo { 
    void foo_impl(){std::cout<<"Bar foo"<<std::endl;} 
    public: 
     Bar(){vtable.foo_ptr = &Bar::foo_impl;} 
     void foo2(){std::cout<<"Bar foo2"<<std::endl;} 
}; 

因此,當你調用虛函數的地址在虛函數表先擡頭,因此,如果您分配一個Bar bar; Foo& foo = bar;,然後foo.foo()電話Bar S版的的foo()代替Foo版本「。