C++友元

友元是C++提供的一种破坏数据隐蔽和封装的机制

1.友元函数

友元函数是在类中使用关键字friend修饰的非成员函数

1.1友元普通函数

定义与概念
  • 友元函数是一个普通的函数
  • 友元普通函数在实现时,不需要类名的限定;在调用时,也不需要由实例来调用
示例代码
#include <iostream>
#include <cmath>
using namespace std;

class Point{
public:
    Point(int x = 0,int y = 0):x(x),y(y){}
    int getX(){ return x;}//内联函数
    int getY(){ return y;}
    void showData();
    friend float dist(Point &p1,Point &p2);//声明友元函数
private:
    int x,y;
};

//普通成员函数的实现,需要类名限定
void Point::showData(){
    cout << "x: " << x << ", y: " << y << endl;
}

//因为友元函数是非成员函数,所以不需要类名限制
float dist(Point &p1,Point &p2){
    double x = p1.x - p2.x;
    double y = p1.y - p2.y;
    return static_cast<float>(sqrt(x*x + y*y));
}

int main()
{
    Point p1(1,1),p2(4,5);
    p1.showData();
    p2.showData();
    //调用友元普通函数时,也不需要由类的实例来调用
    cout << "the distance is : " << dist(p1,p2) << endl;
    return 0;
}

1.2友元成员函数

定义与概念
  • 友元函数是其它类的成员函数
  • 必须先定义包含成员函数的类(比如说A),再在另外一个类(比如说B)中将该成员函数声明为友元函数。此时虽然这个友元函数是A的成员函数,该友元函数仍然称为非成员函数(对于B来说)
示例代码
#include <iostream>
#include <cmath>
using namespace std;


//前向引用声明,否则报错 error: 'Point' does not name a type
class Point;

class Line{
public:
    //这时不能使用这样的形式进行初始化:Line(Point p1,Point p2):p1(p1),p2(p2){}
    //因为此时Point的结构尚未定义,error: field 'p1' has incomplete type
    Line(Point p1,Point p2);
    Point& getP1(); //把引用当做函数返回值
    Point& getP2();
    float dist();
private:
    //不可以这样定义成员变量:Point p1,p2;因为此时Point结构尚不完善
    Point &rp1,&rp2;//类(引用)的组合
};

class Point{
public:
    Point(int x = 0,int y = 0):x(x),y(y){}
    int getX(){ return x;}//内联函数
    int getY(){ return y;}
    void showData();
    //声明友元成员函数
    friend float Line::dist();
private:
    int x,y;
};

void Point::showData(){
    cout << "x: " << x << ", y: " << y << endl;
}

//Line类函数的延迟实现开始

//当一个类的成员变量是引用时,需要在初始化列表中初始化引用
//否则报错:error: uninitialized reference member
Line::Line(Point p1,Point p2):rp1(p1),rp2(p2){}
Point& Line::getP1(){
     return rp1;
}
Point& Line::getP2(){
     return rp2;
}
float Line::dist(){
    double x = rp1.x - rp2.x;
    double y = rp1.y - rp2.y;
    return static_cast<float>(sqrt(x*x + y*y));
}
//Line类函数的延迟实现结束


int main()
{
    Point p1(1,1),p2(4,5);
    p1.showData();
    p2.showData();
    Line line(p1,p2);
    cout << "the distance is : " << line.dist() << endl;
    return 0;
}

2.友元类

定义与概念

示例代码

#include <iostream>
#include <cmath>
using namespace std;

class Point{
public:
    Point(int x = 0,int y = 0):x(x),y(y){}
    int getX(){ return x;}//内联函数
    int getY(){ return y;}
    void showData();
    //声明友元类,否则编译不通过,error: 'int Point::x' is private
    friend class Line;
private:
    int x,y;
};

void Point::showData(){
    cout << "x: " << x << ", y: " << y << endl;
}


class Line{
public:
    Line(Point p1,Point p2):p1(p1),p2(p2){}
    Point getP1(){ return p1;}
    Point getP2(){ return p2;}
    float dist();
private:
    Point p1,p2;//类的组合
};

float Line::dist(){
    double x = p1.x - p2.x;
    double y = p1.y - p2.y;
    return static_cast<float>(sqrt(x*x + y*y));
}

int main()
{
    Point p1(1,1),p2(4,5);
    p1.showData();
    p2.showData();
    Line line(p1,p2);
    cout << "the distance is : " << line.dist() << endl;
    return 0;
}

3.友元的性质

  • 友元关系是不能传递的,如A是B的友元,B是C的友元,但A不是C的友元
  • 友元关系是单向的,A是B的友元,A可以访问B的私有属性,反之不成立
  • 友元关系是不被继承的,A是B的友元,但A的派生类不是B的友元

4.总结

  • 友元是一种破坏数据隐蔽和封装的机制,这是它的用处也是它的坏处。应该尽量避免使用友元
  • 示例代码
#include <iostream>
#include <cmath>
using namespace std;

class Point{
public:
    Point(int x = 0,int y = 0):x(x),y(y){}
    int getX(){ return x;}//内联函数
    int getY(){ return y;}
    void showData();
    float dist(Point p);
private:
    int x,y;
};

void Point::showData(){
    cout << "x: " << x << ", y: " << y << endl;
}

//通过这样的方式,可以避免使用友元
float Point::dist(Point p){
    double x = p.x - this->x;
    double y = p.y - this->y;
    return static_cast<float>(sqrt(x*x + y*y));
}

int main()
{
    Point p1(1,1),p2(4,5);
    p1.showData();
    p2.showData();
    cout << "the distance is : " << p1.dist(p2) << endl;
    return 0;
}

5.补充

5.1前向声明

  • 在C++里面可以声明一个类而不定义它。这个声明被称为前向声明(forward declaration)。
  • 在声明之后,定义之前,这个类是一个不完全类型(incompete type),即已知它是一个类型,但不知道包含哪些成员,具有哪些操作。
  • 不完全类型只能以有限方式使用,不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
原文地址:https://www.cnblogs.com/Libinkai/p/10622473.html