c++ 友元

本文深入探讨了C++中的友元机制,包括其如何削弱类的封装性,以及如何通过友元类和友元函数访问类的私有属性。详细解释了友元函数与普通函数的区别,并通过代码示例展示了如何利用友元函数访问类的成员。文章还阐述了友元机制的优点和缺点,强调了其在现代编程中的限制。

复制自 http://www.cnblogs.com/skyczw/archive/2008/05/27/1208194.html

 

《windows环境多线程编程原理与应用》中解释: 如果将类的封装比喻成一堵墙的话,那么友元机制就像墙上了开了一个门,那些得 到允许的类或函数允许通过这个门访问一般的类或者函数无法访问的私有属性和方法。友元机制使类的封装性得到消弱,所以使用时一定要慎重。友元类的说明将外 界的某个类在本类别的定义中说明为友元,那么外界的类就成为本类的“朋 友”,那个类就可以访问本类的私有数据了。

class Merchant
{
private :
    int m_MyMoney;
    int m_MyRoom;
    … …
public:
    Friend class Lawyer;
    Int getmoney();
    … …
};

class Lawyer
{
private:
    … …
public:
    … …
};

只有你赋予某个类为你的友元时,那个类才有访问你的私有数据的权利。
说明一个函数为一个类的友元函数则该函数可以访问此类的私有数据和方法。定义方法是在类的定义中,在函数名前加上关键字friend.

《挑战30天C/C++》这样解释:
在说明什么是友元之前,我们先说明一下为什么需要友元与友元的缺点:
通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为public(共用的),然而这做带来的问题遍是任何外 部函数都可以毫无约束的访问它操作它,c++利用friend修饰符,可以让一些你设定的函数能够对这些保护数据进行操作,避免把类成员全部设置成 public,最大限度的保护数据成员的安全。友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的 效率,但所矛盾的是,即使是最大限度大保护,同样也破坏了类的封装特性,这即是友元的缺点,在现在cpu速度越来越快的今天我们并不推荐使用它,但它作为 c++一个必要的知识点,一个完整的组成部分,我们还是需要讨论一下的。在类里声明一个普通数学,在前面加上friend修饰,那么这个函数就成了该类的 友元,可以访问该类的一切成员。
下面我们来看一段代码,看看我们是如何利用友元来访问类的一切成员的

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
using namespace std;
class Internet
{
public:
    Internet(char *name,char *address)   // 改为:internet(const char *name , const char *address)
    {
        strcpy(Internet::name,name);
        strcpy(Internet::address,address);
    }
    friend void ShowN(Internet &obj);   //友元函数的声明
public:              // 改为:private
    char name[20];
    char address[20];
};
void ShowN(Internet &obj)        //函数定义,不能写成,void Internet::ShowN(Internet &obj)
{
    cout<<obj.name<<endl;          //可访问internet类中的成员
}
void main()
{
    Internet a("中国软件开发实验室","www.cndev-lab.com");
    ShowN(a);
    cin.get();
}

上面的代码通过友元函数的定义,我们成功的访问到了a对象的保护成员name,友元函数并不能看做是类的成员函数,它只是个被声明为类友元的普通函数,所 以在类外部函数的定义部分不能够写成void Internet::ShowN(Internet &obj),这一点要注意。

一个普通函数可以是多个类的友元函数,对上面的代码我们进行修改,注意观察变化:

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
using namespace std;
class Country;
class Internet
{
public:
    Internet(char *name,char *address)        // 改为:internet(const char *name , const char *address)
    {
        strcpy(Internet::name,name);
        strcpy(Internet::address,address);
    }
    friend void ShowN(Internet &obj,Country &cn);//注意这里
public:
    char name[20];
    char address[20];
};
class Country
{
public:
    Country()
    {
        strcpy(cname,"中国");
    }
    friend void ShowN(Internet &obj,Country &cn);//注意这里
protected:
    char cname[30];
};

void ShowN(Internet &obj,Country &cn)
{
    cout<<cn.cname<<"|"<<obj.name<<endl;
}
void main()
{
    Internet a("中国软件开发实验室","www.cndev-lab.com");
    Country b;
    ShowN(a,b);
    cin.get();
}

一个类的成员函数函数也可以是另一个类的友元,从而可以使得一个类的成员函数可以操作另一个类的数据成员,我们在下面的代码中增加一类Country,注意观察

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
using namespace std;
class Internet;
class Country
{
public:
    Country()
    {
        strcpy(cname,"中国");
    }
    void Editurl(Internet &temp)               ;//成员函数的声明
protected:
    char cname[30];
};
class Internet
{
public:
    Internet(char *name,char *address)
    {
        strcpy(Internet::name,name);
        strcpy(Internet::address,address);
    }
    friend void Country::Editurl(Internet &temp); //友元函数的声明
protected:
    char name[20];
    char address[20];
};
void Country::Editurl(Internet &temp)        //成员函数的外部定义
{
    strcpy(temp.address,"edu.cndev-lab.com");
    cout<<temp.name<<"|"<<temp.address<<endl;
}
void main()
{
    Internet a("中国软件开发实验室","www.cndev-lab.com");
    Country b;
    b.Editurl(a);
    cin.get();
}

整个类也可以是另一个类的友元,该友元也可以称做为友类。友类的每个成员函数都可以访问另一个类的所有成员

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#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();
}

<think>好的,我现在需要回答用户关于C++函数和类的定义及使用方法的问题。首先,我需要回忆自己关于的知识,并确保理解正确。 首先,函数是什么?根据引用中的内容,函数是一个普通函数,不属于类的成员函数,但可以访问类的私有和保护成员。比如在引用[1]的示例代码中,`geta`函数被声明为类A的,因此可以直接访问A的私有成员a。同时,引用[3]提到函数需要在类内部声明,使用`friend`关键字,然后在类外定义。这一点很重要,声明函数的位置在类的内部,而定义则可以在外部。 然后是类。类意味着另一个类的所有成员函数都可以访问当前类的私有和保护成员。例如,如果类B是类A的类,那么B的所有函数都能访问A的私有成员。这在引用[2]和引用[1]中都有提到,但具体的例子可能需要进一步补充。 接下来,用户可能想知道如何正确使用函数和类。例如,在引用[4]中,提到了函数的定义格式,但用户需要注意函数并不是成员函数,所以定义时不需要使用类作用域运算符(即不需要`类名::`)。不过,这里可能存在混淆,因为引用[4]的说法可能有误,正确的函数定义应该是在类外作为普通函数定义,而不是用类名限定。比如引用[3]中的`showX`函数,定义时并没有使用`MyClass::`,而是直接作为全局函数定义。所以需要纠正引用[4]中的描述,可能是用户引用中的错误信息。 此外,的使用场景和注意事项也需要说明。比如,破坏了封装性,应谨慎使用。通常用于运算符重载或某些需要跨类访问的情况。比如重载`<<`运算符时,常将其声明为函数,以便访问私有成员输出内容。 在回答时,需要结构清晰,分点说明函数和类的定义、使用方法,并给出示例代码。同时,结合引用的内容,正确引用来源,例如引用[1]、引用[3]中的代码示例,引用[2]中的定义解释等。 还要注意用户提供的引用中有可能存在错误,需要仔细核对。例如引用[4]提到的函数定义格式是否准确,根据C++标准,函数如果是全局函数,定义时不需要类名限定,而如果是另一个类的成员函数作为,则定义时需要相应的类名限定。所以可能需要更详细地解释这一点,避免用户误解。 最后,生成相关问题,确保问题与主题相关,能够帮助用户进一步学习,比如的优缺点、使用场景、与成员函数的区别等。</think>### C++函数与类详解 #### 一、函数 1. **定义** 函数是一个**非成员函数**,但被声明为类的后,可以直接访问该类的私有(`private`)和保护(`protected`)成员。通过在类内使用`friend`关键字声明,例如: ```cpp class MyClass { private: int x; public: friend void showX(MyClass obj); // 声明函数[^3] }; ``` - **关键点**:函数不是类的成员函数,没有`this`指针,需通过对象参数访问成员。 2. **用法示例** ```cpp #include <iostream> using namespace std; class A { public: A(int _a) : a(_a) {} friend int geta(A &ca); // 声明函数[^1] private: int a; }; // 定义函数(无需类作用域::) int geta(A &ca) { return ca.a; // 直接访问私有成员a } int main() { A a(3); cout << geta(a) << endl; // 输出3 return 0; } ``` - **说明**:`geta()`在类外定义,但能访问类A的私有成员`a`[^1]。 --- #### 二、类 1. **定义** 类是指一个类(如`B`)被声明为另一个类(如`A`)的后,**类B的所有成员函数**均可访问类A的私有和保护成员。 ```cpp class A { friend class B; // 声明类[^2] private: int secret; }; class B { public: void revealSecret(A &obj) { cout << obj.secret; // 直接访问A的私有成员 } }; ``` 2. **使用场景** - 当两个类需要紧密协作(如数据结构和其迭代器)时。 - 需跨类操作私有数据,但又不希望暴露给全局函数。 --- #### 三、注意事项 1. **封装性破坏** 机制破坏了封装性,过度使用会导致代码耦合性高,应谨慎使用[^3]。 2. **单向性** 关系是单向的。例如,若`B`是`A`的类,`A`不自动成为`B`的。 3. **不可继承** 关系不可继承,子类不会自动成为父类的。 --- #### 四、典型应用场景 1. **运算符重载** 例如重载`<<`运算符实现自定义输出: ```cpp class Point { private: int x, y; public: friend ostream& operator<<(ostream &os, const Point &p); }; ostream& operator<<(ostream &os, const Point &p) { os << "(" << p.x << ", " << p.y << ")"; // 访问私有成员 return os; } ``` 2. **跨类协作** 如树(`Tree`)类与节点(`Node`)类,节点类可能需要访问树的私有成员。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值