c++之友元

本文详细介绍了C++中的友元机制,包括友元的作用、使用场景、如何使用友元函数和友元类。通过实例展示了友元如何提高程序效率,同时探讨了友元对封装性和安全性的潜在影响。友元函数和友元类的声明与调用方式也得到了阐述,以及友元关系的特点。

1.为什么要引用友元

提高程序运行效率,既减少了类型和安全性检查及调用时间的开销,但它破坏了类的封装型和隐藏性,使得非成员函数可以访问类的私有成员。

2.使用场景

1)运算符重载的某些场合;

2)2个类要共享数据

3)异类若想要访问private的数据,必须声明为友元

3.使用友元函数

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

由于友元函数不是成员函数,所以不存在this指针,必须通过对象来访问。

友元仅是打破了外部访问中的private的权限。A声明为B的友元,A就可以通过B的对象访问B的私有成员。

1)要访问非static成员,需要对象做参数;

2)要访问static成员或全局变量,则不需要对象做参数;

3)如果做参数的对象是全局对象,则不需要对象做参数;

友元函数的位置

        是类外函数,所以它的声明可以放在类的私有段或公有段且没有区别。

友元函数的调用

        可以直接调用友元函数,不需要通过对象或指针。

4.实战

4.1 普通函数做友元函数

class Pos
{
public:
    friend Pos MovePos(Pos &current, Pos &move);

    Pos(int x=0, int y=0, int angle=0)
        :m_x(x)
        ,m_y(y)
        ,m_angle(angle)
    {}
    ~Pos()
    {}


    void PrintPos()
    {
        cout << "current pos : ( " << m_x << ", " << m_y << ", " << m_angle << ")" << endl;
    }

private:
    int m_x;
    int m_y;
    int m_angle;
};

Pos MovePos(Pos &current, Pos &move)
{
    Pos temp;
    temp.m_x = current.m_x + move.m_x;
    temp.m_y = current.m_y + move.m_y;
    temp.m_angle = current.m_angle + move.m_angle;
    return  current;
}
int main()
{
    Pos one(1,2,3);
    Pos two(10,20,30);
    Pos three = MovePos(one,two);

    three.PrintPos();
    return 0;
}

4.2 成员函数做友元函数

1)友元函数是其它类的成员函数

2)必须先定义包含成员函数的类(比如说A),再在另外一个类(比如说B)中将该成员函数声明为友元函数。此时虽然这个友元函数是A的成员函数,该友元函数仍然称为非成员函数(对于B来说)

前向声明:是一种不完全的类型(incomplete type)声明,即只需要提供类名(无需提供实现)即可。

特点:

1)不能定义对象

2)仅可用于定义指向这个类型的指针或引用

3)仅用于声明不是定义,仅可以作为形参类型或函数的返回值类型(定义在类外实现)

#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;//类(引用)的组合
    //这里也可以用Point *rp1, *rp2;
    //这样在构造函数时,可以不用传入Point的对象,而是在Line的构造函数中动态分配内存
    //即
    Line(int x, int y, int y, int y1){
        rp1 = new Point(x, y);
        rp2 = new Point(x1, y1);
    }
 
};
 
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;
}

4.3 友元类

#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;
}

5.友元性质

1)友元关系是不能传递的,如A是B的友元,B是C的友元,但A不是C的友元

2)友元关系是单向的,A是B的友元,A可以访问B的私有属性,反之不成立

3)友元关系是不被继承的,A是B的友元,但A的派生类不是B的友元

<think>我们被要求回答关于C++函数和类的声明与使用。根据引用内容,我们可以总结如下: 1. 函数:在类内部声明,使用关键字`friend`,该函数可以不是该类的成员函数,但能访问类的私有和保护成员。 2. 类:在类A中声明类B为类,则类B的所有成员函数都可以访问类A的私有和保护成员。 我们将按照以下步骤进行说明: - 函数的声明与使用 - 类的声明与使用 - 注意事项(如封装性的破坏) 同时,我们还需要生成相关问题。 注意:所有行内数学表达式用$...$,独立公式用$$...$$,但本问题不涉及数学公式,所以主要关注代码示例。</think>### C++ 函数与类的声明与使用 在 C++ 中,机制允许特定外部函数或类访问类的私有成员和保护成员。以下是具体实现方法: #### 一、函数的声明与使用 1. **在类内部声明函数** 使用 `friend` 关键字声明函数: ```cpp class MyClass { private: int secretValue; public: MyClass(int val) : secretValue(val) {} // 声明函数 friend void displaySecret(const MyClass& obj); }; ``` 2. **定义函数(无需作用域限定符)** ```cpp void displaySecret(const MyClass& obj) { // 可直接访问私有成员 std::cout << "Secret value: " << obj.secretValue << std::endl; } ``` 3. **使用示例** ```cpp int main() { MyClass obj(42); displaySecret(obj); // 输出: Secret value: 42 } ``` #### 二、类的声明与使用 1. **在类A中声明类B为类** ```cpp class ClassA { private: int hiddenData; public: ClassA(int data) : hiddenData(data) {} // 声明类 friend class ClassB; }; ``` 2. **类可访问所有私有成员** ```cpp class ClassB { public: void showData(const ClassA& a) { // 直接访问ClassA的私有成员 std::cout << "Hidden data: " << a.hiddenData << std::endl; } }; ``` 3. **使用示例** ```cpp int main() { ClassA a(99); ClassB b; b.showData(a); // 输出: Hidden data: 99 } ``` ### 关键注意事项 1. **封装性破坏** 机制会破坏封装性,应仅在必要时使用(如运算符重载或紧密协作的类)[^3]。 2. **单向关系** 关系不可传递:若类A是类B的,类B是类C的,类A**不能**访问类C的私有成员[^4]。 3. **声明位置无关** 声明可出现在类的任何区域(`public`/`private`/`protected`),效果相同[^2]。 4. **非成员特性** 函数**不是**类的成员函数,因此调用时无需通过对象实例[^1]。 ### 典型应用场景 - 运算符重载(如实现 `<<` 输出运算符) - 跨类协作的紧密耦合组件 - 需要访问多个类私有成员的全局函数 ```cpp // 运算符重载示例 friend std::ostream& operator<<(std::ostream& os, const MyClass& obj) { os << "Value: " << obj.secretValue; return os; } ``` > ⚠️ 过度使用会降低代码可维护性,优先考虑通过公共接口实现访问[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值