C++友元函数详解:访问私有成员的正确方式

方予
方予
发布时间:2026-10-15 | 3人浏览过
原创

C++ 中的 `friend`(友元)机制允许非类成员的外部函数或类直接访问当前类的私有(private)和受保护(protected)成员。这通常用于打破严格的封装原则,以便特定的外部实体能够操作内部数据。本文将通过代码实例,分别演示如何声明普通全局函数、特定成员函数以及整个类为友元,并介绍实际应用场景。

为什么需要友元函数

在面向对象的 C++ 编程中,数据封装是核心原则之一。将成员变量设为 private 可以防止外部意外修改数据。然而,在某些场景下(如运算符重载、工具函数),我们需要两个不同的类之间,或者一个外部工具函数能够访问某个类的私有数据。此时,friend 关键字便派上了用场。它仅出现在函数声明处,告诉编译器:虽然该函数不属于此类,但我信任它可以访问我的私有属性。

普通全局函数作为类的友元

这是最基础的友元形式。只需在类体内部使用 friend 前缀声明一个外部全局函数即可。该函数不需要属于该类,也不带 this 指针,但可以通过对象参数访问所有成员。

第1步:定义包含私有成员的类

首先创建一个模拟房间的类 Room,其中卧室为私有,客厅为公开:

// 房间类
class Room {
    // 声明好基友visit为友元函数
    friend void goodGayVisit(Room &room);
private:
    string bedRoom; // 卧室,私有
public:
    string sittingRoom; // 客厅,公开
    
    Room() {
        this->bedRoom = "卧室";
        this->sittingRoom = "客厅";
    }
};

第2步:实现友元函数

在全局作用域定义该函数。由于它是友元,它可以像类内函数一样访问 bedRoom:

// 普通全局函数作为类的友元
void goodGayVisit(Room &room) {
    cout << "好基友访问了你的" << room.sittingRoom << endl;
    cout << "好基友访问了你的" << room.bedRoom << endl; // OK,因为是友元
}

第3步:调用测试

void test01() {
    Room myRoom;
    goodGayVisit(myRoom); // 成功输出卧室信息
}

类的成员函数作为另一个类的友元

当 A 类的某个成员函数需要访问 B 类的私有数据时,可以将该成员函数声明为 B 类的友元。需要注意两点:
1. 如果 A 类在 B 类之前未定义,需进行前向声明。
2. 在友元声明中,必须加上 A 类的类名作用域,即 A::functionName。

完整代码实现

#include <iostream>
using namespace std;

class Room; // 前向声明

class GoodGay {
public:
    void visit1(Room &room); // 非友元成员
    void visit2(Room &room); // 将设为友元成员
};

class Room {
    // 将 GoodGay 的 visit2 成员函数声明为友元
    friend void GoodGay::visit2(Room &room);
private:
    string bedRoom;
public:
    string sittingRoom;
    Room() {
        this->bedRoom = "卧室";
        this->sittingRoom = "客厅";
    }
};

// visit1 只能访问公开成员
void GoodGay::visit1(Room &room) {
    cout << "visit1访问了你的" << room.sittingRoom << endl;
    // cout << room.bedRoom; // Error: 无法访问私有数据
}

// visit2 是友元,可访问所有成员
void GoodGay::visit2(Room &room) {
    cout << "visit2访问了你的" << room.sittingRoom << endl;
    cout << "visit2访问了你的" << room.bedRoom << endl; // OK
}

int main() {
    Room myRoom;
    GoodGay gg;
    gg.visit1(myRoom); // 仅访问客厅
    gg.visit2(myRoom); // 客厅卧室皆可访问
    return 0;
}

整个类作为另一个类的友元

如果希望类 A 的所有成员函数都能访问类 B 的私有数据,可以直接将类 A 声明为类 B 的友元。这种方式称为“类级友元”。同样,若类 A 尚未定义,需要先进行前向声明。

#include <iostream>
using namespace std;

class Room; // 前向声明

class GoodGay {
public:
    void visit1(Room &room);
    void visit2(Room &room);
};

class Room {
    // 将 GoodGay 整个类声明为友元
    friend class GoodGay;
private:
    string bedRoom;
public:
    string sittingRoom;
    Room() {
        this->bedRoom = "卧室";
        this->sittingRoom = "客厅";
    }
};

// 此时 GoodGay 的所有成员函数都可访问 Room 的私有数据
void GoodGay::visit1(Room &room) {
    cout << "visit1:" << room.bedRoom << endl; // OK
}

void GoodGay::visit2(Room &room) {
    cout << "visit2:" << room.bedRoom << endl; // OK
}

int main() {
    Room myRoom;
    GoodGay gg;
    gg.visit1(myRoom);
    gg.visit2(myRoom);
    return 0;
}

友元关系的三大特性

在使用友元时,必须清楚其局限性,避免在设计中出现逻辑错误:

  1. 友元关系不能被继承:如果父类的一个外部函数是友元,子类并不自动继承该关系。
  2. 友元关系是单向的:A 是 B 的朋友,不代表 B 也是 A 的朋友。
  3. 友元关系不具有传递性:B 是 A 的朋友,C 是 B 的朋友,并不代表 C 是 A 的朋友。

实战:封装电视机与遥控器

为了更直观地理解友元的应用,我们构建一个 “电视机” 类和一个 “遥控器” 类。遥控器需要能够直接调整电视机的内部状态(如音量值、频道值),这些值通常应被隐藏起来。

第一步:实现基础电视机类

在不使用友元的情况下,电视机只能通过公共接口(如 onOrOff())改变状态,无法提供底层数据修改能力。

class TV {
    enum { On, Off };
    enum { minVol, maxVol = 100 };
    enum { minChannel = 1, maxChannel = 255 };
private:
    int mState;
    int mVolume;
    int mChannel;
public:
    TV() : mState(Off), mVolume(minVol), mChannel(minChannel) {}
    
    void onOrOff(void) { mState = (mState == On ? Off : On); }
    void volumeUp(void) { if(mVolume <= maxVol) mVolume++; }
    void volumeDown(void) { if(mVolume >= minVol) mVolume--; }
    void channelUp(void) { if(mChannel <= maxChannel) mChannel++; }
    void channelDown(void) { if(mChannel >= minChannel) mChannel--; }
    
    void showTVState(void) {
        cout << "State:" << (mState==On?"On":"Off") << endl;
        cout << "Volume:" << mVolume << endl;
        cout << "Channel:" << mChannel << endl;
    }
};

第二步:引入遥控器类并使用友元

我们在 TV 类中声明 Remote 为友元。这样,遥控器不仅可以调用电视机的公共方法,还可以直接设置私有成员变量 mChannel,从而提供更强大的功能(如指定频道跳转)。

class TV {
    friend class Remote; // 授予遥控器最高权限
    // ... (枚举和私有成员同上)
private:
    int mState;
    int mVolume;
    int mChannel;
public:
    // ... (构造函数和公共方法同上)
};

class Remote {
private:
    TV *pTv;
public:
    Remote(TV *pTv) : pTv(pTv) {}
    
    void setChannel(int num) {
        // 利用友元身份,直接操作私有成员
        if(num >= TV::minChannel && num <= TV::maxChannel) {
            this->pTv->mChannel = num;
        }
    }
    
    void onOrOff(void) { this->pTv->onOrOff(); }
    void volumeUp(void) { this->pTv->volumeUp(); }
    // 其他公共方法类似...
};

void test() {
    TV tv;
    Remote remote(&tv);
    remote.onOrOff();
    remote.setChannel(75); // 直接设定频道
    remote.showTVState();
}

总结

C++ 友元是一种强大的打破封装的工具,适用于密切相关的组件间共享数据。使用时请务必权衡安全性,优先选择具体的函数友元或按需使用的类友元,而不是无节制地暴露私有数据。以上就是 C++ 友元函数的详细内容,更多关于 C++ 面向对象技术与访问控制的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表