C++雾中风景1:友元类和面向对象

因为连续准备入职的合作社都盼望能转C++,所以近年来吧是以部就班的初步进行C++的求学。然后这个系列之章打算追究C++的语言特征,也比一下见仁见智语言(如Java,Scala,Python,Go)之间的筹划哲学,同时为抛砖引玉的要能发生大拿们的指。最近在学习过程里接触了友元函数和友元类的概念,第一篇文章我们不怕聊聊友元的概念。

1.友元函数:

开赛先简单介绍一下友元这个定义吧。
每当C++之中,类的友元函数是概念在相近外部,但它发且访问类的具备民用(private)成员与保护(protected)成员。尽管友元函数的原型有在类的定义着起了,但是友元函数并无是成员函数。友元可以是一个函数,该函数被称友元函数;友元也得以是一个近乎,该类被称为友元类。

直接上单代码,我们看看友元函数和友元类具体是什么使用的:

#include <iostream>
using namespace std;

class Box {
public:
    Box(double l, double b, double h) {
        length = l;
        breadth = b;
        height = h;
    }
    friend class A;
    friend void boxPrintBox(Box &box);
private:
    double length;
    double breadth;
    double height;
};

//友元函数,友元函数并非成员函数
void boxPrintBox(Box &b) {
    cout << b.height << " " << b.length << " " << b.breadth << endl;
}

//友元类
class A {
public:
    void printBox(Box &b) {
        cout << b.height << " " << b.length << " " << b.breadth << endl;
    }
};


int main() {
    Box box(1,2,3);

    //友元函数,可以访问Box类的private的变量
    boxPrintBox(box);
    //友元类,同样可以访问Box类的private的变量
    A a;
    a.printBox(box);

    return 0;
}

点的代码可以看,友元函数和友元类都可一直访问到对象的个体变量。接下来我们来分析一下友元函数的特点。

  • 1、为什么要引入友元函数:
    于贯彻类似中数据共享时,减少系统出,提高效率。具体来说:为了要其他类似的分子函数直接访问该类的私有变量。即:允许外面的类或函数去访问类的个体变量和保护变量,从而使少数个像样共享同一函数。能够提高效率,表达简单、清晰
  • 2、什么时使用友元函数:
    1)运算符重载的一点场合用以友元。
    2)两个像样设共享数据的时候
  • 3、友元方式的缺点:
    1)友元函数破环了打包机制,除非万不得已之图景下才下友元函数。

2.友首关系和面向对象:

连下我们聊怎么打面向对象的角度去理解友元关系。(以下内容皆为私家知道,有不可靠之处在通向能指正

  • 1)友元函数
     友元函数是免由属于及类似的函数,除了能访问类的私变量之外,与其余实现在相近外部的函数无异。从面向对象的角度看,函数是免应有单独实现让类以外的。显然独立及类似以外的友元函数,从面向对象的角度来考虑,是勿优雅的解决方法。
     这与C++本身兼容C语法有关,如操作符<<的重载利用的就算是友元函数。<<的函数重载如下,这个函数是独和类似以外实现之计:

    friend ostream &operator<<(ostream &output, const object &o)
    

      显然,这个<<的函数方法还应有打属于在ostream这个看似中,做呢一个不过重载的办法实现。如下概念

    friend ostream &operator<<(const object &o) //作为ostream的类函数
    

      如Java,Scala,Python都无支持独立和类似存在定义的函数了。在面向对象的角度,后续的语言实现的愈发纯粹了。所以若自己代码风格趋近与面向对象的风骨,就该尽量理由友元类来贯彻内需之成效,而未是使友元函数。
      出情侣私信我说:Python之中明明一直def就得定义函数了,也未待接近呀,这是匪是吗非合乎面向对象的逻辑思考?这里大概解释一下,Python之中的每个函数,都见面于包成一个function对象,所以Python之中是成套都对象的,不会见有独立及类似存在的函数。而要同Java与Scala之中的lambda表达式,也是包装也躲名类存在的。

  • 2)友元类
    经过友元类包装之中,友元类摇身一变,类中有的点子还改成友元函数了。看起连无见面坏上文提到的面向对象的逻辑了,但是涉及到后续又存部分中的坑,我们一并来捋一捋:
      友元关系匪可知继续。基类的友元对派生类的分子没有例外访问权限。如果基类被赋予友元关系,则只有基类具有独特访问权限,该基类的派生类非可知访问给友元关系的近乎。
      好复杂,我们直接上代码:

    #include
    using namespace std;

    class A {
    private:

    int x;
    

    friend class C;
    };

    class B:public A{
    private:

    int y;
    

    };

    class C {

    void printA(A& a) {
        cout << a.x << endl;
    }
    void printB(B& b) {
        cout << b.x << endl; //C类依然可以利用友元关系访问从B类从A类继承来的私有变量
        //cout << b.y << endl; C类不可以访问B类的私有变量,友元关系不继承,该语句不合法。
    }
    

    };

明显,类C与A的友元关系止步于继承处,类C没法看类B新定义的个人变量。(此地留下一个稍微问题被大家,如果类B覆盖了类A的民用变量x,类C之中的printB是否还是可经编译呢?)

俺们再拘留同样段不同之代码:

#include <iostream>
using namespace std;

class A
{
    int x;
public:
    friend class B;
};
class B
{
public:
    void fun(A& a){ cout << a.x << endl;}
};

class C:public B
{
public:
    //void fun2(A& a){ cout <<a.a <<endl;}   //派生类新加的函数却不能访问A,此句会报错
};

void main()
{
    A a;
    C c;
    c.fun(a); //C是B的派生类,只能基类B的函数fun来访问A的对象
}

类C虽然连续了类B,但是呢不在颇具了和A的友元关系,只能”拼爹”。依赖从类B之中继承的友元函数来访问类A。(此地同样留一个不怎么题目为大家,如果类B之中的fun函数是protected或private的,那上述代码还会健康编译吗?)

  于这边召开一个简约的总:友元关系在友元类之中无继承性,只能借助基类的友元关系。

3.非C++语言是怎么化解友元关系之:

  • Java
    JAVA修饰符类型(public,protected,private)
  • public的接近、类属变量和措施,包内以及包外的其他类似都好看;
  • protected的近乎、类属变量和办法,包内的任何类,及包外的那些继承了此类的子类才能够看;
  • private的好像、类属变量和方法,包内包外的任何像样都无克看;
  • 只要一个类、类属变量和办法无因即时三种修饰符来修饰。那么包内的外类似都得看它,而包外的另类似都不克看它(包括包外继承了此类的子类)。所以这种类型有时也叫做friendly类型C++,现在清楚这名字的出处了咔嚓,大家对同一个package之中要加大怎么类产生木有新的认识了邪?

  • Scala
    在Scala之中,private和protected可以指定额外的参数。可以应用private[AccessQualifier],AccessQualifier可以是this,也堪是外的类名或包名。这样便得这样理解:这个成员对所有类都是private,除了自己跟AccessQualifier所表示范围外的接近。这个概念吗是可以递推的,也就是说,如果AccessQualifier是一个接近,那么private成员对AccessQualifier的AccessQualifier也是可见的。
    哼优雅的主意啊,我爱Scala。

  • Python
    父他喵的远非访问控制,全负自觉。

  • Golang
    于野蛮,就指首字母的深浅写区分。没法做到细粒度的支配,不过看起呢不影响大部分观的工落实。所以是否如此化繁为简的计划哲学,也是相同栽优雅的筹划也罢?