天天看點

C++ 虛函數和友元

虛函數具有動态聯編性,在類族中有強大功能;友元函數具有跨類通路的功能,本質卻是一種對封裝的破壞。

先看這樣一個例子:

#include<iostream>
using namespace std;
class A;
class B
{
private:
    int x;
    void print()
    {
        cout<<x<<endl;
    }
public:
    B(int i = 0)
    {
        x = i;
    }
    friend class A;
};
class A
{
public:
    void func(B b)
    {
        b.print();
    }
};
class D: public B
{
public:
    D(int i):B(i) {}
};
int main()
{
    cout<<sizeof(A)<<" "<<sizeof(B)<<" "<<sizeof(D)<<endl;
    D d(99);
    A a;
    a.func(d);
    return 0;
}      

程式執行結果為:

        1 4 4

        99

上例中,A是B的友元類,A中的所有成員函數都為B的友元函數,可通路B的私有成員函數。友元類A大小為1,基類和派生類大小都是4,友元類A不是基類B的一部分,更不是派生類D的一部分。

 從上例看,友元似乎能夠被繼承,A的函數func這能通路B的派生類D嘛!這不基類的友元函數或友元類能夠通路派生類的私有成員!

但若将上例中的繼承關系改為私有繼承,則:

class D: private B
   a.func(d);  // error C2243: “類型轉換”: 從“D *”到“const B &”的轉換存在,但無法通路      

我們知道:public繼承是一種“is a”的關系,即一個派生類對象可看成一個基類對象。是以,上例中不是基類的友元被繼承了,而是派生類被識别為基類了。

再比如這樣一個例子

#include<iostream>
using namespace std;
class B;
class A
{
private:
    void print()
    {
        cout<<"A::print"<<endl;
    }
public:
    friend class B;
};
class B
{
public:
    void func(A a)
    {
        a.print();
    }
};
class D: public B { };

int main()
{
    A a;
    D d;
    d.func(a);
    return 0;
}      

        A::print

上例中,B為A的友元類,D是B的派生類,D繼承了基類B的友元函數func,它能通路A的私有成員。由此可知一個友元類的派生類,可以通過其基類接口去通路設定其基類為友元類的類的私有成員,也就是說一個類的友元類的派生類,某種意義上還是其友元類。

但若在上例D中新增加個成員函數,該函數是不能通路A私有成員的。

class D: public B
{
public:
        void test(A a){ a.print(); } // error C2248: “A::print”: 無法通路 private 成員(在“A”類中聲明)
};      
#include<iostream>
using namespace std;
class A;
class B
{
private:
    void print()
    {
        cout<<"B::print"<<endl;
    }
public:
    friend class A;
};
class A
{
public:
    void func(B b)
    {
        b.print();
    }
};
class D: public B
{
private:
    void print()
    {
        cout<<"D::print"<<endl;
    }
};
int main()
{
    D d;
    A a;
    a.func(d);
    return 0;
}      

    B::print

和前兩例類似,友元關系并沒有被繼承,僅是派生類對象當成了一個基類對象來用,是以輸出“B::print”。

若将上例print函數改為虛函數并通過多态來通路,就可以達到類似于友元可以繼承的效果。

class A;
class B
{
private:
    virtual void print()
    {
        cout<<"B::print"<<endl;
    }
public:
    friend class A;
};
class A
{
public:
    void func(B* pb)
    {
        pb->print();
    }
};
class D: public B
{
private:
    virtual void print()
    {
        cout<<"D::print"<<endl;
    }
};

int main()
{
    D d;
    A a;
    a.func(&d);
    return 0;
}      

 這本質上就是滿足了多态的三個條件:

必須存在繼承關系;   

繼承關系中必須有同名的虛函數,并且它們是覆寫關系。  

存在基類的指針,通過該指針調用虛函數。

作者:王陸