当前位置: 首页 > news >正文

C++入门自学Day4-- c++类与对象(友元)

c++类与对象往期回顾:

        c++类与对象(类的初始化和静态成员)

        c++类与对象(赋值运算符与拷贝构造)

        c++类与对象(拷贝构造)

        c++类与对象(构造和析构函数)

        c++类与对象(初识2)

        c++类与对象(初识)

  

友元

   一、友元的定义

        友元是通过在类中使用关键字 friend 声明的函数、另一个类或类的成员函数,它可以访问该类的私有和保护成员,即使它不属于该类。

 二、为什么需要友元?

        原因总结

  1. 1、跨类访问私有成员:在两个类需要深度交互但又不希望暴露所有成员变量时,可以使用友元访问特定内容。

  2. 2、重载运算符(如 I/O)时需要访问私有成员:例如重载 <<、>>。

  3. 3、提高效率:比使用公共接口函数访问更直接,开销小。

  4. 4、简化设计:避免为单个函数暴露大量 getter/setter。

三、友元的三种形式

        1、友元函数(最常见)

        一个独立函数被声明为某个类的友元,可以访问该类的私有成员。

友元函数的简单实现
//类的定义
class Date{friend void Print(const Date& d);//友元函数的声明public:Date(int year = 2025,int month = 8,int day = 1){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
//友元函数的定义
void Print(const Date& d){cout<<d._year<<"-"<<d._month<<"-"<<d._day<<endl;// 访问私有成员
}int main(){Date d;Print(d);
}

类的运算符"<<"的友元实现

        cout的类型是系统的ostream类,我们之前是如何使用我们的“<<”的呢?

 cout<<d._year<<"-"<<d._month<<"-"<<d._day<<endl;

那么如何实现该类的运算符重载呢?

       "<<"运算符的两个参数:1、Date日期类,2、ostream类。其中ostream类是第一个操作数-->第一个参数,Date日期类是第二个操作数-->第二个参数。

如果把其放在类中实现:

class Date{friend void Print(const Date& d);public:Date(int year = 2025,int month = 8,int day = 1){_year = year;_month = month;_day = day;}void operator<<(ostream& out){out<<_year<<"-"<<_month<<"-"<<_day<<endl;}private:int _year;int _month;int _day;
};
void Print(const Date& d){cout<<d._year<<"-"<<d._month<<"-"<<d._day<<endl;
}

主函数调用:

int main(){Date d;d<<cout;
}

这里我们在使用该运算符“<<”,需要把d作为左操作数,cout作为右操作数。则与我们默认的<<用法“cout<<d._year<<"-"<<d._month<<"-"<<d._day<<endl;”相反,那么这里我们就需要通过友元来实现:


友元实现: “记得传入变量的引用”

class Date{friend ostream& operator<<(ostream& out,Date& d);public:Date(int year = 2025,int month = 8,int day = 1){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out,Date& d){out<<d._year<<"-"<<d._month<<"-"<<d._day<<endl;return out;
}

主函数调用:

int main(){Date d;cout<<d;
}

输出描述:

2025-8-1

利用友元实现就可以将 ostream类作为左操作数。


2、 友元类

        一个类被另一个类声明为友元类,可以访问后者的私有成员。

代码实现:

        这里我们定义了Date是Time的好朋友,所以Date可以访问Time的私有变量。注意顺序不要搞混了。

class Time{friend class Date;public:Time(int hour = 16,int minute = 11,int second = 59):_hour(hour),_minute(minute),_second(second){}private:int _hour;int _minute;int _second;
};class Date{public:Date(int year = 2025,int month = 8,int day = 1):_year(year),_month(month),_day(day){_t._hour = 12;}void Print(){cout<<_year<<"-"<<_month<<"-"<<_day<<"-"<<_t._hour<<"-"<<_t._minute<<"-"<<_t._second<<endl;}private:int _year;int _month;int _day;Time _t;
};

主函数调用:


int main(){Date d1;Time t;d1.Print();
}

输出描述:

2025-8-1-12-11-59


3、 友元成员函数

        某个类的某个成员函数是另一个类的友元。

代码实现:

class A;class B {
public:void printA(const A& a);  // 只声明
};class A {
private:int value = 123;friend void B::printA(const A&);  // 指定 B 的某个成员函数为友元
};void B::printA(const A& a) {std::cout << a.value << std::endl;
}

四、友元的特性

特性

说明

单向性

A 是 B 的友元,不代表 B 是 A 的友元

非继承性

子类不会继承父类的友元关系

破坏封装性

友元机制打破了类的私有保护机制,所以应慎用

不能被对象调用

友元函数不是类成员,不能通过对象来调用它(除非类中有转发函数)


五、内部类(天生的友元类)

        1、内部类的定义:

        内部类是定义在另一个类内部的类,属于外部类的成员类型之一。

代码实现:

class Outer {
private:int secret = 42;public:class Inner {  // 内部类public:void show() {std::cout << "I'm Inner class!" << std::endl;}};
};

那么如何来定义一个内部类对象呢?

Outer::Inner obj;
obj.show();

        2、内部类的注意事项:

        ❌ 不能直接访问外部类!内部类不是外部类的成员函数,它只是作用域在外部类中,所以默认无法访问外部类的私有成员,除非你显式地授予权限。      

方法:friend class Inner;  // 👈 把 Inner 声明为友元  把内部类声明为友元类即可


六、使用友元的注意事项

  • ✔️ 控制最小访问范围:只给确实需要访问的函数/类加 friend。

  • ❌ 不要滥用友元:频繁使用友元会破坏类的封装性,使得代码耦合度高,难以维护。

  • 📦 与封装结合使用:友元应作为封装的补充机制,而非替代品。


七、总结一句话

        友元机制是 C++ 提供的一种“例外访问权”,用于在特定情况下允许外部函数或类访问某个类的私有或保护成员,以提高效率、简化操作、增强协作,但使用时应谨慎控制访问范围,避免滥用破坏封装性。

http://www.lryc.cn/news/606793.html

相关文章:

  • 《软件测试与质量控制》实验报告一 测试用例设计
  • 新一代PLC控制软件平台EsDA-AWStudio
  • Linux文件系统理解2
  • Python爬虫库性能与选型实战指南:从需求到落地的全链路解析
  • PendingIntent的flag和原理解析
  • 如何使用淘宝开放平台API获取商品详细信息?
  • Docker设置容器时间
  • 通过pendingIntent启动activity被block问题
  • 网站QPS多少才算高并发
  • TOGAF指南1
  • Effective C++ 条款16: 成对使用new和delete时要采用相同形式
  • pycharm快捷键设置为和vscode一样
  • 数据仓库、数据湖与湖仓一体技术笔记
  • 高防服务器租用:保障数据安全
  • 自建rustdesk服务器过程记录
  • 【代码】印章提取红色部分
  • 观测云基于 ToB/ToC 业务可观测最佳实践
  • Android ConstraintLayout 使用详解
  • A 常见图形API和图形渲染引擎介绍
  • k8s云原生rook-ceph pvc快照与恢复(上)
  • 提问总结1
  • 3. boost::asio之同步读写的客户端和服务器示例
  • Syzkaller实战教程5: 初始种子加载机制剖析第一集
  • “数据管理” 一场高风险的游戏
  • CSS Text(文本)详解
  • 【工具变量】上市公司企业突破性创新及渐进性创新数据集-含处理带代码(2012-2024年)
  • Shell脚本批量检测IP的443端口联通性
  • DB-GPT 0.7.3 版本更新:支持Qwen3 Embedding和Reranker模型、支持知识库自定义检索策略等
  • 从0开始学习R语言--Day64--决策树回归
  • 登录校验一