把类的成员函数声明为友元函数,但不能访问私有成员的原因和解决办法

定义类X,类Y,在类X中声明类Y的成员函数g()为类X的友元函数

常见的几种错误写法及原因:

错误写法1

class Y;
class X
{
public:
    X(int i) :i(i) {}
    friend void Y::g(X& object);
    void  print()
    {
        cout << i;
    }
private:
    int i;
};

class Y
{
public:
    void g(X& object)
    {
        object.i += 1;
    }
};

结果编译器显示 Y 的成员函数g()无法访问类X的私有成员i:在这里插入图片描述
原因:在类X中声明Y的成员函数为友元时,此时类Y还没有定义。第一行的class Y,仅作为声明告诉编译器有这个类,而不知道这个类究竟包含了什么。所以在类X中声明成员函数g()为友元的语句无效

错误写法2

通过对刚刚写法的分析,我们很容易想到把类Y的定义放到类X前以消除这个错误,但是这样可行吗?

class X;
class Y
{
public:
    void g(X& object)
    {
        object.i += 1;
    }
};
class X
{
public:
    X(int i) :i(i) {}
    friend void Y::g(X& object);
    void  print()
    {
        cout << i;
    }
private:
    int i;
};

此时编译器显示,类Y的成员函数g()仍然无法访问类X的私有成员
在这里插入图片描述

原因与上一个错误类似。在类Y中定义g()函数时,编译器并不知道g()作为了类X的友元,因为此时类X还没有定义,而只是在第一行作了声明。

正确写法:

class X;


class Y
{
public:
    void g(X& object);
};

class X
{
public:
    X(int i) :i(i) {}
    friend void Y::g(X& object);
    friend  class Z;
    friend void h(X& object);
    void  print()
    {
        cout << i;
    }
private:
    int i;
};

void Y::g(X& object)
{
    object.i += 1;
}

通过把成员函数g()的定义写在类外,可以避免出现以上两种情况

总结:
编译器的编译顺序是从上至下,像这种相互使用的用法,只能先声明,最后统一实现。避免了在这一方使用那一方时出现那一方还未定义而报错的情况。

补充:
这是否说明类的友元函数只能在类外定义呢? 答案是 否定的:当定义在类Y里的友元函数不访问类x的私有成员时,这样定义没问题。

### C++ 友元函数无法访问私有成员原因及解决方法 #### 1. 原因分析 友元函数本身并不是成员函数,而是独立于之外的一个全局函数。尽管它被授予了特殊的权限来访问私有成员保护成员,但它并不具备 `this` 指针[^2]。这意味着,在调用友元函数时,不会自动传递当前对象作为参数。因此,如果要通过友元函数访问某个特定对象的私有成员,则需要显式地将该对象作为参数传入。 当尝试让友元函数访问中的私有成员失败时,通常是因为未正确传递目标对象或者错误地试图像成员函数那样直接操作数据成员。例如: ```cpp class MyClass { private: int value; public: MyClass(int v) : value(v) {} friend void showValue(); // 错误示范:缺少必要的参数支持 }; void showValue() { std::cout << "Value: " << value << std::endl; // 编译器报错:value 是未知标识符 } ``` 上述代码会引发编译错误,因为 `showValue()` 函数不知道应该作用在哪一个具体的实例上[^4]。 --- #### 2. 解决方案 为了使友元函数能够成功访问指定对象的私有成员,需将其设计成接受相应型的参数形式。以下是修正后的版本及其解释: ##### 修改版示例 ```cpp #include <iostream> using namespace std; class MyClass { private: int value; public: MyClass(int v) : value(v) {} // 正确声明方式:带参友元函数 friend void showValue(const MyClass& obj); }; // 定义友元函数并接收具体对象作为参数 void showValue(const MyClass& obj) { cout << "Value: " << obj.value << endl; // 成功访问私有成员 'value' } int main() { MyClass instance(42); // 调用友元函数,并提供明确的对象引用 showValue(instance); return 0; } ``` 在此修改中,`showValue` 接受了一个常量引用 (`const MyClass&`) 参数,从而明确了哪个对象的数据应当暴露给外部世界。这样既保留了封装原则又实现了预期功能[^3]。 另外需要注意的是,虽然整个可能定义多个不同签名的友元函数,但只要某次声明赋予了某种型的操作权柄,那么这些权利覆盖范围内的所有非公有的部分都将变得可用[^5]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值