友元函数

1、首先,我们为什么要使用友元呢?

答:类具有封装和信息隐藏的特性只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

为了解决上面的问题,提出了友元函数。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend友元不是成员函数,但是它可以访问类中的私有成员。

优点:提高程序的运行效率

缺点:破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

2、那么,什么是友元函数呢?

答:友元函数就是可以直接访问类中私有成员的函数,虽然它不是成员函数。

3、使用友元需要注意什么呢?

答:1)如果类A是类B的友元,则类A(的成员函数)可以直接访问类B的私有 成员。

2)友元不能继承。也就是说,类A是类B的友元,类D是类B的派生类,则类A并不会直接是类D的友元。通俗一点,父亲的朋友,并不天生就是儿子的朋友。即:父类的友元,并不会因为继承,而成为派生类的友元。

 

友元类:

友元除了前面讲过的函数以外,友元还可以是类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数。

示例代码如下:

#include<iostream>    
using namespace std; 
class Internet; 
class Country 
{ 
public: 
    Country() 
    { 
        strcpy(cname,"中国"); 
    } 
friend class Internet;//友类的声明 
protected: 
    char cname[30]; 
}; 
class Internet 
{ 
public:   
    Internet(char *name,char *address)   
    {   
        strcpy(Internet::name,name);   
        strcpy(Internet::address,address);  
    } 
void Editcname(Country &temp); 
protected:   
    char name[20]; 
    char address[20]; 
}; 
void Internet::Editcname(Country &temp) 
{ 
    strcpy(temp.cname,"中华人民共和国");  
} 
void main() 
{ 
    Internet a("中国软件开发实验室","www.cndev-lab.com"); 
    Country b; 
    a.Editcname(b); 
    cin.get(); 
}

在上面的代码中我们成功的通过InternetEditcname成员函数操作了Country类的保护成员cname

当友元遇到了虚函数:

来看下面的几段代码:

Code:

class A;  
class B  
{  
private:  
    virtual void output()  
    {  
        cout << "B::output" << endl;  
    }  
   friend class A;       
};  
  
class D : public B  
{  
private:  
    virtual void output()  
    {  
        cout << "D::output" << endl;  
    }      
};  

 B 的友元类, DB的派生类。 所以,若想在A中直接访问D的代码,则编译不过:

Code:

class A  

{  

public:       

    void test()  

    {  

        D d;  

        d.output(); //编译出错  

    }  

};  

这一点大家都没觉得有问题,毕竟书上写得都明白直观:父类的友元,并不会因为继承,而成为派生类的友元。

但若代码改成这样,编译器似乎就被欺骗了:

Code:

class A  

{  

public:      

    void test()  

    {  

        D d;  

        B* pb = &d;     

        pb->output(); //编译通过  

    }  

};  

没错,很多人会认为这种代码,就算能通过编译器,也很可能是一种不好的代码,因为它怎么看都像是在欺骗编译器。是这样吗?先不讨论。先问一个问题: 上面的08行代码,output调用的是B类的那个output,还是D类的那个呢?

回答正确并不难——既然会认定这段代码带有“欺骗”性质,而且又注意到output是一个“虚函数”的话——就能能正确地解答: 调用的是D类的。A明明只是B的友元,但却通过一个简单的类型转换,就访问了D类的那个私有函数,所以会觉得这是一种“欺骗”。

如果这是一种欺骗,那我们先来回答这个骗局为什么能成立:因为“友元”的判断(resolve),在编译期决定;而虚函数在运行期去resolve。在编译08行代码时,编译器看到*pb的类型是B,而AB的友元,所以允许它调用output(它认为是B::output);而在运行时,由于output是虚函数,所以最终被决定到D::output头上。

 

内容参考:http://blog.chinaunix.net/uid-21411227-id-1826758.html

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值