一、友元机制概述

在C++中,友元(Friend)是一种特殊的机制,它允许一个类或函数访问另一个类的私有(private)和保护(protected)成员。友元打破了类的封装性,但有时这种"破坏"是必要的,特别是在需要提高某些特定函数或类的访问权限时。

友元的关键字是friend,C++支持三种友元形式:

  1. 全局函数作为友元
  2. 类作为友元
  3. 成员函数作为友元

二、全局函数作为友元

2.1 基本概念

全局函数作为友元是最简单的友元形式,它允许一个普通的全局函数访问类的私有成员。

2.2 代码示例

class People {
    friend void friendVisit(People* p); // 声明全局函数为友元
    
public:
    People() {
        _house = "别墅";
        _car = "法拉利";
    }
    
public:
    string _house;
    
private:
    string _car;
};

// 全局函数定义
void friendVisit(People* p) {
    cout << "好朋友来拆你的家啦!!!" << p->_house << endl;
    cout << "好朋友来开你车啦!!!" << p->_car << endl; // 可以访问私有成员
}

2.3 特点分析

  1. 需要在类内部使用friend关键字声明友元函数
  2. 友元函数不是类的成员函数,但可以访问类的所有成员
  3. 友元关系是单向的,不具有传递性

三、类作为友元

3.1 基本概念

当一个类被声明为另一个类的友元时,这个类的所有成员函数都可以访问另一个类的私有和保护成员。

3.2 代码示例

class People {
    friend class peoplefriend; // 声明整个类为友元
    
public:
    People() {
        _house = "别墅";
        _car = "法拉利";
    }
    
public:
    string _house;
    
private:
    string _car;
};

class peoplefriend {
public:
    void visit(People* p) {
        cout << "好朋友来拆你的" << p->_house << endl;
        cout << "好朋友来拆你的" << p->_car << endl; // 可以访问私有成员
    }
};

3.3 特点分析

  1. 友元关系是单向的:A是B的友元,不意味着B是A的友元
  2. 友元关系不具有传递性:A是B的友元,B是C的友元,不意味着A是C的友元
  3. 友元关系不能被继承

四、成员函数作为友元

4.1 基本概念

这是最精细的友元控制方式,只允许另一个类的特定成员函数访问本类的私有成员。

4.2 代码示例

class People; // 前向声明

class peoplefriend1 {
public:
    void visitall(People* p); // 将被声明为友元的成员函数
    void visitpub(People* p); // 普通成员函数
};

class People {
    friend void peoplefriend1::visitall(People* p); // 声明特定成员函数为友元
    
public:
    People() {
        _house = "别墅";
        _car = "法拉利";
    }
    
public:
    string _house;
    
private:
    string _car;
};

// 成员函数定义
void peoplefriend1::visitall(People* p) {
    cout << "好朋友访问了你的" << p->_house << "并摧毁了它!!!" << endl;
    cout << "好朋友访问了你的" << p->_car << "并摧毁了它!!!" << endl; // 可以访问私有成员
}

void peoplefriend1::visitpub(People* p) {
    cout << "好朋友访问了你的" << p->_house << "并摧毁了它!!!" << endl;
    // cout << p->_car << endl; // 错误!不能访问私有成员
}

4.3 特点分析

  1. 需要前向声明(forward declaration)
  2. 只能指定特定的成员函数为友元,其他成员函数不能访问私有成员
  3. 语法较为复杂,需要注意定义顺序

五、友元机制的注意事项

  1. 慎用友元​:友元破坏了封装性,应谨慎使用。只有在确实需要让外部函数或类访问私有成员时才使用。

  2. 前向声明问题​:在成员函数作为友元时,需要注意类的定义顺序,可能需要前向声明。

  3. 友元不可继承​:友元关系不能被继承,基类的友元不是派生类的友元。

  4. 友元不可传递​:A是B的友元,B是C的友元,并不意味着A是C的友元。

  5. 重载函数​:如果需要将一组重载函数声明为友元,必须对每一个函数单独声明。

六、友元机制的应用场景

  1. 运算符重载​:特别是需要访问私有成员的操作符重载,如<<、>>等。

  2. 某些工具函数​:需要访问类内部状态但不适合作为成员函数的工具函数。

  3. 紧密协作的类​:某些设计模式中,两个类需要紧密协作,可能需要互相访问私有成员。

  4. 测试代码​:在单元测试中,测试代码可能需要访问被测类的私有成员。

七、总结

友元机制是C++中一种强大的特性,它提供了对类封装性的有限突破。合理使用友元可以提高代码的灵活性和效率,但过度使用会破坏面向对象的设计原则。开发者应当根据实际情况,权衡封装性和便利性,做出适当的设计选择。

在实际开发中,建议:

  1. 优先考虑使用公有成员函数提供接口
  2. 在确实需要时才使用友元
  3. 尽量使用最精细的访问控制(如成员函数友元而非整个类友元)
  4. 充分注释说明使用友元的原因

通过本文的详细讲解和代码示例,相信读者已经对C++的友元机制有了全面而深入的理解。在实际编程中,应根据具体需求合理运用这一特性。

八:源码及运行图

#include<iostream>
#include<string>
using namespace std;
/*全局函数作为友元
* 目的让一个类或函数可以访问另外一个类的私有成员
* 友元的关键字 friend
* 三种友元:
* 1.全局作为友元
* 2.类作为友元
* 3.成员函数作为友元
*/

/*
成员函数作为友元
//peoplefriend的某个函数可以访问 people的私有成员变量

*/
class People;//前向声明
class peoplefriend1// 1. 首先定义 peoplefriend1 类,但只声明其成员函数,将定义放在People类之后
{
public:peoplefriend1()
{
}
	  void visitall(People* p);
	  void visitpub(People* p);
	  
};
class People
{
	friend  void peoplefriend1::visitall(People* p);
	friend class peoplefriend;//加了下面类报错取消
	friend void friendVisit(People* p);//加了以后下面报错取消
	
public:
	People(){
		_house = "别墅";
		_car = "法拉利";
	}

public:
	string _house;
private:
	string _car;

};
void friendVisit(People* p)
{

	cout << "好朋友来拆你的家啦!!!"<<p->_house << endl;
	cout << "好朋友来开你车啦!!!" <<p->_car<< endl;//报错无法访问,因为是私有成员
}


/*
类作为友元:
让一个类去访问另外一个类的私有成员
*/
class peoplefriend
{
public:peoplefriend()
	{

	}
	void visit(People* p)
	{
		cout << "好朋友来拆你的" << p->_house << endl;
		cout << "好朋友来拆你的" << p->_car << endl;//私有成员无法访问
	}
};
void peoplefriend1::visitall(People* p)
{
	cout << "好朋友访问了你的" << p->_house << "并摧毁了它!!!" << endl;
	cout << "好朋友访问了你的" <<p->_car  << "并摧毁了它!!!" << endl;
}
void peoplefriend1::visitpub(People* p)
{
	cout << "好朋友访问了你的" << p->_house << "并摧毁了它!!!" << endl;
	
}

int main()
{
	People p;
	friendVisit(&p);//传入p的地址
	peoplefriend p1;//实例化
	p1.visit(&p);
	peoplefriend1 p2;
	p2.visitall(&p);
	p2.visitpub(&p);

	return 0;
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐