C++友元详解

1.什么是友元
在一个类A中,将类B声明为友元类,则类B可以访问类A的私有成员和保护成员。另外,也可以将函数声明为友元函数。

2.什么时候用到友元
若不同的类之间某些共享数据成员,可以使用友元,简化类的设计。

3.友元类
友元类的声明:friend class 类名
示例如下:
对于电视和遥控器来说,都共享频道数据成员和开关操作,这时可以将遥控器声明为电视的友元类,则遥控器可以直接访问电视的数据成员,且一个遥控器可以控制多台电视,这则例子很好的体现了友元的特点。

tv.h

#ifndef TV_H_
#define TV_H_
class Tv
{
public :
    friend class Remote;
    enum State{off,on};

    Tv(int s=off,int mc=125):state(s),maxchannel(mc),channel(2){}

    void onoff(){state=(state==on)?off:on;}
    void chanup();
    void chandown();
    void settings() const;
private:
    int state;
    int maxchannel;
    int channel;
};
class Remote{
public:
    Remote(){};
    void onoff(Tv &t){t.onoff();};
    void chanup(Tv &t){t.chanup();};
    void chandown(Tv &t){t.chandown();};
    void set_chan(Tv &t,int c){t.channel=c;};
};

tv.cpp

#include<iostream>
#include "tv.h"
void Tv::chanup()
{
    if(channel<maxchannel){
        channel++;
    }
    else{
        channel = 1;
    }
}
void Tv::chandown()
{
    if(channel>1){
        channel--;
    }
    else{
        channel = maxchannel;
    }
}
void Tv::settings() const
{
    using std::cout;
    using std::endl;
    cout<<"Tv is "<<(state==on?"on":"off")<<endl;
    if(state==on)
    {
        cout<<"channel:"<<channel<<endl;
    }
}

use_tv.cpp 可使用同一个遥控器控制多台不同的电视

#include<iostream>
#include "tv.h"
int main()
{
    using std::cout;
    using std::endl;
    Tv s42;
    cout<<"Initing Tv s42......"<<endl;
    s42.settings();
    s42.onoff();
    s42.chanup();
    cout<<"adjusted Tv s42....."<<endl;
    s42.settings();

    Remote control;
    control.set_chan(s42,10);
    control.volup(s42);
    cout<<"settings after using remote......"<<endl;
    s42.settings();
    getchar();
    return 0;
}

示例可以看出,若不使用友元,则必须将Tv类的私有部分设置为共有的,或者创建一个大型类来包含电视和遥控器。这种解决方法也无法反应,一个遥控器可以用于多台电视。

4.友元函数
从上例可以看出,Remote类只有set_chan方法使用到了Tv类的私有成员,因此我们可以让Remote::set_chan()成为Tv类的友元函数,而不必让整个类成为友元。

tvfm.h

#ifndef TV_H_
#define TV_H_
class Tv;
class Remote{
public :
    enum State{off,on};
private:
    int mode;
public:
    Remote(){};
    void onoff(Tv &t);
    void chanup(Tv &t);
    void chandown(Tv &t);
    void set_chan(Tv &t,int c);
};
class Tv
{
public :
    friend void Remote::set_chan(Tv &t,int c);
    enum State{off,on};

    Tv(int s=off,int mc=125):state(s),maxchannel(mc),channel(2){}

    void onoff(){state=(state==on)?off:on;}
    void chanup();
    void chandown();
    void settings() const;
private:
    int state;
    int maxchannel;
    int channel;
};

inline void Remote::onoff(Tv &t){t.onoff();}
inline void Remote::chanup(Tv &t){t.chanup();}
inline void Remote::chandown(Tv &t){t.chandown();}
inline void Remote::set_chan(Tv &t,int c){t.channel=c;}
#endif

5.共同的友元
多个类需要访问同一个函数,则可以在这些类中将这个函数声明为友元函数。例如:有一个计数器函数counter,两个类A和B同时调用这个函数,则可以在类A和B中将函数counter声明为友元函数。

void counter()
{
    //....
}
class A
{
    friend int counter();
}
class B
{
    friend int counter();
}

6.使用友元类时注意:
1) 友元关系不能被继承。
2)友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元。
3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元

参考资料:《C++ Primer.Plus》 pp.602-610

时间: 2024-10-15 12:43:24

C++友元详解的相关文章

C++友元实例详解

通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为public,然而这做带来的问题遍是任何外部函数都可以毫无约束的访问它操作它,c++利用friend修饰符,可以让一些你设定的函数能够对这些保护数据进行操作,避免把类成员全部设置成public,最大限度的保护数据成员的安全. 友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的效率,但矛盾的是,即使是最大限度的保护,同样也破坏了类的封装特性,这即是友元的缺点

C++:虚函数的详解

5.4.2 虚函数详解 1.虚函数的定义 虚函数就是在基类中被关键字virtual说明,并在派生类重新定义的函数.虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数. 虚函数的定义是在基类中进行的,它是在基类中需要定义为虚函数的成员函数的声明中冠以关键字virtual.定义虚函数的格式如下: virtual 函数类型 函数名(形参表) {     函数体 } 在基类中的某个成员函数声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新

详解设计模式六大原则

设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的:设计模式使代码编制真正工程化:设计模式是软件工程的基石脉络,如同大厦的结构一样. 借用并改编一下鲁迅老师<故乡>中的一句话,一句话概括设计模式: 希望本无所谓有,无所谓无.这正如coding的设计模式,其实coding本没有设计模式,用的人多了,也便成了设计模式 v六大原

运算符重载详解

1.运算符重载定义: C++中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作.这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作.运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一. 运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作.运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operato

C++ Namespace 详解

命名空间的定义格式为:(取自C++标准文档) named-namespace-definition: namespace identifier { namespace-body } unnamed-namespace-definition: namespace { namespace-body } namespace-body: declaration-seqopt 有名的命名空间: namespace 命名空间名 { 声明序列可选 } 无名的命名空间: namespace { 声明序列可选 }

c/c++面试题(6)运算符重载详解

1.操作符函数: 在特定条件下,编译器有能力把一个由操作数和操作符共同组成的表达式,解释为对 一个全局或成员函数的调用,该全局或成员函数被称为操作符函数.该全局或成员函数 被称为操作符函数.通过定义操作符函数,可以实现针对自定义类型的运算法则,并使之 与内置类型一样参与各种表达式运算. 2.首先我们先介绍下左值和右值,因为我们在运用运算符的时候要尽量和内置类型的一致性. 左值:有名的可以直接取地址的我们称之为左值,左值的特性是可以修改的. 右值:右值主要是一些临时变量,匿名变量,字符串字面值常量

Anatomy of a Program in Memory—剖析内存中的一个程序(进程的虚拟存储器映像布局详解)

(进程的虚拟存储器映像布局详解) 前言:原文来自于http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory/ 这里只是对其进行翻译,并且重构了原文中的图片.译注则是我增加的内容,用来解释原文或提出问题:由于个人水平有限,译文和译注中的错误之处还请广大坛友提出指正,不胜感激. 下面采用分段中英对照的方式列出内容: Memory management is the heart of operating systems; i

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h