【C++入门】Cyber霓虹镜像城的跨域通行证 —— 【友元(friend)】跨类协作破坏封装性?友元函数与友元类为你架起特权桥梁!

⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


WARNING : DETECTING HIGH ENERGY

🌊 🌉 🌊 心手合一 · 水到渠成

|------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| >>> ACCESS TERMINAL <<< ||
| 🦾 作者主页 | 🔥 C语言核心 |
| 💾 编程百度 | 📡 代码仓库 |


Running Process: 100% | Latency: 0ms


索引与导读

友元函数

友元函数的出现缘由

  • C++的封装性将数据隐藏在类内部,但有时外部函数需要直接访问类的私有成员

    🚩友元函数解决了这一矛盾

  • 当外部函数需要频繁访问类的私有数据时,通过公有接口(getter/setter)会产生函数调用开销,友元函数可以直接访问,效率更高

  • 某些操作符(如<<>>)必须作为非成员函数重载,但又需要访问私有成员

  • 当两个或多个类需要共享数据,但这些类之间没有继承关系时,友元函数可以作为桥梁


友元函数的基本定义

cpp 复制代码
class ClassName {
private:
    // 私有成员
public:
    // 声明友元函数
    friend returnType functionName(parameters);
};

1)friend 关键字

╔═█▓▒░ CODE CORE 🔥
┌─────────────┐
friend关键字 │friend returnType functionName(parameters);
└─────────────┘

  • friend 是一个关键字,表示后面声明的是友元函数

  • 只在类内部声明时使用,在函数定义时不需要

  • 友元关系是授予的,不是索取来的

2)函数返回类型 (returnType)

cpp 复制代码
friend void functionName();           // 无返回值
friend int functionName();            // 返回int
friend MyClass functionName();        // 返回自定义类型
friend const string& functionName();  // 返回常量引用
friend auto functionName();           // C++14起可用auto

3)函数名 (functionName)

cpp 复制代码
// 可以是普通函数
friend void printData(const ClassName& obj);

// 可以是操作符函数
friend ostream& operator<<(ostream& os, const ClassName& obj);

// 可以是模板函数
template<typename T>
friend void templateFunc(T value);

4)参数列表 (parameters)

4.1)传值
cpp 复制代码
friend void func(ClassName obj);       // 传值
  • 典例
cpp 复制代码
friend void testStatus(Light light) {
    light.brightness = 100; // 仅修改副本,原灯亮度不变
}
4.2)传引用

用途: 直接修改原始对象

cpp 复制代码
friend void func(ClassName& obj);      // 传引用
  • 典例
cpp 复制代码
friend void turnOn(Light& light) {
    light.isOn = true; // 直接改变原始灯的状态
}
4.3)传常量引用

用途: 高效读取。不产生拷贝(省内存),且保证函数内部不会意外修改数据

cpp 复制代码
friend void func(const ClassName& obj);// 传常量引用
  • 典例
cpp 复制代码
friend void printInfo(const Light& light) {
    // 只能读取,尝试修改 light.isOn 会报错
    cout << "亮度: " << light.brightness << endl;
}
4.4)传指针

用途: 处理"可选"对象,可以传递 nullptr 表示没有设备

cpp 复制代码
friend void func(ClassName* obj);
  • 典例
cpp 复制代码
friend void repair(Light* light) {
    if (light) { // 检查指针是否为空
        light->isBroken = false;
    }
}
4.5)多个参数

用途: 处理两个类之间的交互

cpp 复制代码
friend void connect(ClassA& a, ClassB& b);
  • 典例
cpp 复制代码
// 在 Remote 和 Light 类中分别声明为友元
friend void pairDevice(Remote& r, Light& l) {
    r.deviceID = l.id; // 遥控器获取灯的私有 ID
    l.isPaired = true; // 灯标记为已配对
}
4.6)带默认参数

用途: 增加函数的灵活性,提供一种"常用模式",同时允许特殊配置

cpp 复制代码
friend void process(ClassName& obj, int mode = 0);
  • 典例
cpp 复制代码
friend void reset(Light& light, int level = 0) {
    light.brightness = level; // 默认调为0,也可以指定亮度
}

友元函数的特点

  • 突破封装: 允许外部访问私有成员。

  • 声明方式: 使用 friend 关键字,在类内部声明。

  • 非成员函数: 它是定义在类外部的普通函数,不属于该类,因此没有 this 指针。

  • 位置随意: 可以在类定义的任何地方声明 (public/private 区域均可)。

  • 一个函数可以是多个类的友元

cpp 复制代码
#include <iostream>
using namespace std;

class ClassB; // 【补充】:前向声明,告诉编译器 ClassB 稍后会定义

class ClassA {
private:
    int numA;
public:
    ClassA(int n) : numA(n) {}
    // 声明 friendFunction 是本类的友元
    friend int add(const ClassA& a, const ClassB& b);
};

class ClassB {
private:
    int numB;
public:
    ClassB(int n) : numB(n) {}
    // 声明同一个函数 add 也是本类的友元
    friend int add(const ClassA& a, const ClassB& b);
};

// 这个函数同时拥有访问 ClassA 和 ClassB 私有成员的权限
int add(const ClassA& a, const ClassB& b) {
    return a.numA + b.numB;
}

int main() {
    ClassA a(10);
    ClassB b(20);
    cout << "两类私有成员之和: " << add(a, b) << endl;
    return 0;
}

友元类

友元类(Friend Class)C++ 中的一种特殊机制,允许一个类访问另一个类的 私有(private)保护(protected) 成员

1)基本定义

如果一个类 A 将另一个类 B 声明为友元,那么 B 的所有成员函数都可以直接访问 A 的私有和保护成员,就好像这些成员在 B 中是公有的一样

cpp 复制代码
class A {
private:
    int private_data;

    // 声明 B 是 A 的友元类
    friend class B;
};

class B {
public:
    void func(A& obj) {
        obj.private_data = 10; // 允许访问,因为 B 是 A 的友元
    }
};

2)友元关系(友元类)的特性

  • 单向性:如果 A 是 B 的友元,这并不意味着 B 也是 A 的友元
  • 不可传递:如果 A 是 B 的友元,且 B 是 C 的友元,这并不意味着 A 是 C 的友元
cpp 复制代码
class A {
    friend class B; // B 是 A 的朋友,B 可以访问 A
private:
    int dataA;
};

class B {
    friend class C; // C 是 B 的朋友,C 可以访问 B
private:
    int dataB;
public:
    void accessA(A& a) {
        // 正确:因为 B 是 A 的友元
        a.dataA = 10; 
    }
};

class C {
public:
    void accessB(B& b) {
        // 正确:因为 C 是 B 的友元
        b.dataB = 20; 
    }

    // void accessA(A& a) {
        // 【错误】:不可传递!
        // 虽然 C -> B,B -> A,但 C 不能直接访问 A
        // a.dataA = 30; // 编译报错
    // }
};

// 同时也说明了单向性:A 并没有声明 B 是朋友,所以 A 不能访问 B 的私有成员。

💻结尾--- 核心连接协议

警告: 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠


【📡】 建立深度链接: 关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。

【⚡】 能量过载分发: 执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。

【💾】 离线缓存核心: 将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。

【💬】 协议加密解密:评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。

【🛰️】 信号频率投票: 通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。



相关推荐
karry_k28 分钟前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
karry_k35 分钟前
PostgreSQL 在 MyBatis 中执行正常 SQL 失效:一次 DELETE USING 踩坑记录
java·后端
SamDeepThinking4 小时前
从源码到代码:MyBatis-Flex 与 MyBatis-Plus 的逐项对比
java·后端·程序员
她的男孩7 小时前
Spring Boot 接 Flowable 工作流:用 3 个注解搭一个请假审批流程
java·后端·架构
荣码9 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
plainGeekDev10 小时前
Gson → kotlinx.serialization
android·java·kotlin
小bo波19 小时前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
咖啡八杯20 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式