友元类

本文深入探讨了C++中友元类的概念及其应用,通过实例展示了友元类如何允许一个类访问另一个类的私有和保护成员,同时强调了这种设计可能带来的数据安全性风险。例程演示了将遥控器类作为电视机类的友元类,实现遥控器对电视机的操作。文章旨在帮助开发者理解友元类的使用场景和潜在问题。
部署运行你感兴趣的模型镜像

=======================什么是友元类=======================

      当一个类B成为了另外一个类A的“朋友”时,那么类A的私有和保护的数据成员就可以被类B访问。我们就把类B叫做类A的友元。

=======================友元类能做什么=======================

      友元类可以通过自己的方法来访问把它当做朋友的那个类的所有成员。但是我们应该注意的是,我们把类B设置成了类A的友元类,但是这并不会是类A成为类B的友元。说白了就是:甲愿意把甲的秘密告诉乙,但是乙不见得愿意把乙自己的秘密告诉甲。

=======================友元类的声明方法和其用法=======================

      声明友元类的方法其实很简单,只要我们在类A的成员列表中写下如下语句:

friend  class  B;

  这样一来,类B就被声明成了类A的友元。注意,类B虽然是类A的友元,但是两者之间不存在继承关系。这也就是说,友元类和原来那个类之间并没有什么继承关系,也不存在包含或者是被包含的关系,友元类和我上一篇博文《谈谈:C++类的“包含”机制》中的包含是完全不一样的!

=======================友元类的一个具体例子=======================

      在这里,我们引用一个我从网上收集到的例子来说明友元类的作用:假设我们要设计一个模拟电视机和遥控器的程序。大家都之道,遥控机类和电视机类是不相包含的,而且,遥控器可以操作电视机,但是电视机无法操作遥控器,这就比较符合友元的特性了。即我们把遥控器类说明成电视机类的友元。下面是这个例子的具体代码:

#include <iostream>
using  namespace  std;
class  TV
{
     public :
       friend  class  Tele;
       TV():on_off(off),volume(20),channel(3),mode(tv){}
     private :   
       enum {on,off};
       enum {tv,av};
       enum {minve,maxve=100};
       enum {mincl,maxcl=60};
       bool  on_off;
       int   volume;
       int  channel;
       int  mode;
};
class  Tele
{
     public :
        void  OnOFF(TV&t){t.on_off=(t.on_off==t.on)?t.off:t.on;}
        void  SetMode(TV&t){t.mode=(t.mode==t.tv)?t.av:t.tv;}
        bool  VolumeUp(TV&t);
        bool  VolumeDown(TV&t);
        bool  ChannelUp(TV&t);
        bool  ChannelDown(TV&t);
        void  show(TV&t) const ;   
};
bool  Tele::VolumeUp(TV&t)
{
     if  (t.volume<t.maxve)
     {
         t.volume++;
         return  true ;
     }
     else
     {
         return  false ;
     }
}
bool  Tele::VolumeDown(TV&t)
{
     if  (t.volume>t.minve)
     {
         t.volume--;
         return  true ;
     }
     else
     {
         return  false ;
     }
}
bool  Tele::ChannelUp(TV&t)
{
     if  (t.channel<t.maxcl)
     {
         t.channel++;
         return  true ;
     }
     else
     {
         return  false ;
     }
}
bool  Tele::ChannelDown(TV&t)
{
     if  (t.channel>t.mincl)
     {
         t.channel--;
         return  true ;
     }
     else
     {
         return  false ;
     }
}
void  Tele::show(TV&t) const
{
     if  (t.on_off==t.on)
     {
         cout<< "电视现在" <<(t.on_off==t.on? "开启" : "关闭" )<<endl;
         cout<< "音量大小为:" <<t.volume<<endl;
         cout<< "信号接收模式为:" <<(t.mode==t.av? "AV" : "TV" )<<endl;
         cout<< "频道为:" <<t.channel<<endl;
 
     }
     else
     {
         cout<< "电视现在" <<(t.on_off==t.on? "开启" : "关闭" )<<endl;
     }
     
}
int  main()
{
     Tele t1;
     TV t2;
     t1.show(t2);
     t1.OnOFF(t2);
     t1.show(t2);
     cout<< "调大声音" <<endl;
     t1.VolumeUp(t2);
     cout<< "频道+1" <<endl;
     t1.ChannelUp(t2);
     cout<< "转换模式" <<endl;
     t1.SetMode(t2);
     t1.show(t2);
     return  0;
}

      我们在程序的第6行定义了一个TV电视机类的友元类Tele。那么程序中就可以来调用TV类中的私有成员。下面,是该程序的输出:

      好了,这就是友元类了。关于友元类,我反正是这样认为的,因为友元类有可能会破坏数据的安全性,我们还是少用为好啊!在这里我只是记录一下它的用法罢了,呵呵

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think>我们正在讨论C++中的友元类(friend class)。根据引用内容,友元类允许一个类(称为友元类)访问另一个类的私有(private)和保护(protected)成员。这是C++中打破封装性的一种机制,需要谨慎使用。 ### 友元类的基本概念 - **定义**:如果类A将类B声明为友元(即在类A的定义中使用`friend class B;`),那么类B的所有成员函数都可以访问类A的私有成员和保护成员。 - **特性**: 1. **单向性**:友元关系是单向的。如果类A声明类B是它的友元,这并不意味着类A可以访问类B的私有成员(除非类B也声明类A为友元)。 2. **非传递性**:如果类A是类B的友元,类B是类C的友元,这并不意味着类A是类C的友元。 3. **非继承性**:友元关系不能被继承。如果类A是类B的友元,类C继承自类A,那么类C不是类B的友元(除非类B显式声明类C为友元)。 ### 友元类的使用方法 在声明友元类时,通常将`friend`关键字和友元类的类名放在类的定义中。例如: ```cpp class A { private: int private_data; public: // 声明类B是类A的友元类 friend class B; }; class B { public: void accessA(A& a) { // 由于B是A的友元,可以直接访问A的私有成员 std::cout << a.private_data << std::endl; } }; ``` ### 示例代码分析 引用[3]提供了一个引擎(Engine)和汽车(Car)的示例: ```cpp #include <iostream> using namespace std; class Engine { private: int horsepower; public: Engine(int hp) : horsepower(hp) {} // 声明Car为友元类 friend class Car; }; class Car { public: void showEnginePower(const Engine& e) { // 由于Car是Engine的友元,可以直接访问Engine的私有成员horsepower std::cout << "Engine horsepower: " << e.horsepower << std::endl; } }; int main() { Engine engine(500); Car car; car.showEnginePower(engine); // 输出:Engine horsepower: 500 return 0; } ``` 在这个例子中: - `Engine`类将`Car`类声明为友元,因此`Car`类的成员函数`showEnginePower`可以访问`Engine`类的私有成员`horsepower`。 ### 使用注意事项 1. **破坏封装**:友元打破了类的封装性,因此应该只在必要的情况下使用,例如当两个类在逻辑上紧密耦合,且需要频繁访问对方私有成员时。 2. **替代方案**:在可能的情况下,优先考虑使用公有成员函数(getter/setter)来访问私有数据,以保持封装性。 3. **友元声明位置**:友元声明可以在类的任何部分(private、protected或public)出现,因为友元声明不受访问控制符的影响。但通常放在类定义的开始或结束位置以保持清晰。 ### 模板友元类 引用[4]中提到了模板友元的情况。当友元是一个模板类时,需要在友元声明中指定模板参数。例如: ```cpp template<typename T> class A { private: T data; public: // 声明模板类B为友元 template<typename U> friend class B; }; // 这样,对于任何类型U,类B<U>都是A<T>的友元,但注意:通常我们可能需要更精确的控制。 ``` 但是,上述声明意味着对于每一个特化的`A<T>`,所有特化的`B<U>`都是它的友元。如果我们希望只有相同类型的`B<T>`才是`A<T>`的友元,可以这样声明: ```cpp template<typename T> class B; // 前置声明 template<typename T> class A { private: T data; public: // 声明特定特化的B<T>为友元 friend class B<T>; }; ``` ### 总结 友元类提供了一种访问其他类私有成员的机制,但应谨慎使用。在需要两个类紧密协作的情况下,友元类可以避免使用公共接口带来的性能开销或设计复杂性。然而,过度使用友元会破坏封装,增加类之间的耦合度。 §§相关问题§§ 1. 友元函数和友元类有什么区别? 2. 在C++中,如何声明一个模板类作为友元? 3. 使用友元类可能会带来哪些设计上的问题? 4. 除了友元,还有哪些方法可以访问类的私有成员?</think>### C++ 友元类详解 友元类(friend class)是 C++ 中允许特定类访问另一个类私有成员的机制。通过 `friend` 关键字声明,它打破了严格的封装性,在特定场景下可提升代码效率和灵活性。 #### 一、核心特性 1. **单向访问权限** 若类 A 声明类 B 为友元,则 B 可访问 A 的私有成员,但 A **不能**访问 B 的私有成员。 示例: ```cpp class Storage { friend class Display; // 声明友元 private: int secret = 42; }; class Display { public: void show(const Storage& s) { std::cout << s.secret; // 允许访问私有成员 } }; ``` 2. **非传递性** 若 A 是 B 的友元,B 是 C 的友元,**不等于** A 是 C 的友元。 3. **非继承性** 派生类**不继承**基类的友元关系。若基类声明友元,派生类仍需单独声明。 #### 二、使用场景 1. **紧密协作的类** 如引擎(Engine)和汽车(Car)类: ```cpp class Engine { friend class Car; // 声明友元 private: int horsepower; public: Engine(int hp) : horsepower(hp) {} }; class Car { public: void showPower(const Engine& e) { std::cout << "Power: " << e.horsepower; // 直接访问私有成员 } }; ``` [^3] 2. **工具类辅助** 如日志类(Logger)访问多个类的内部状态: ```cpp class Network { friend class Logger; private: int packetCount; }; class Logger { public: static void log(const Network& net) { std::cout << "Packets: " << net.packetCount; } }; ``` #### 三、模板友元类 当涉及模板时,需前置声明并指定模板参数: ```cpp template<typename T> class Box { friend class Inspector<T>; // 声明模板友元 private: T content; }; template<typename T> class Inspector { public: void check(const Box<T>& box) { std::cout << "Content: " << box.content; } }; ``` [^4] #### 四、注意事项 1. **谨慎使用** 过度使用会破坏封装性,增加耦合度。优先考虑公有接口(getter/setter)。 2. **声明位置无关** `friend` 声明可放在类的任何区域(public/private/protected),效果相同。 3. **替代方案** 考虑使用组合(composition)或中介模式(mediator pattern)替代友元。 > **最佳实践**:仅当两个类在逻辑上高度内聚(如引擎与汽车),且频繁访问私有数据时使用友元类。其他场景建议保持封装[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值