使用 friend 关键字和使用成员函数修改类内部的私有变量有什么区别

What is the difference between using the friend keyword, and using a member function to modify private variables inside of a class?

本文关键字:内部 变量 区别 什么 修改 关键字 friend 函数 成员 使用      更新时间:2023-10-16

正如问题所问...

两者

之间有什么区别:

class MyClass
{
public:
    MyClass(){
        m_a = 0;
    }
private:
    int m_a;
    friend void set_a(MyClass &a);
};

void set_a(MyClass &a)
{
    std::cout << a.m_a << std::endl;
    a.m_a = 500;
    std::cout << a.m_a << std::endl;
}

int main(void) {
    MyClass my_class_instance;
    set_a(my_class_instance);
    system("pause");
}

和:

class MyClass
{
public:
    MyClass(){
        m_a = 0;
    }
    void set_a(){
        std::cout << this->m_a << std::endl;
        this->m_a = 500;
        std::cout << this->m_a << std::endl;
    }
private:
    int m_a;
};

int main(void) {
    MyClass my_class_instance;
    my_class_instance.set_a();
    system("pause");
}

它只是函数的首选结构,还是存在真实的、可衡量的差异?据我所知,这两个函数在所有情况下都实现了相同的结果,除了第一个示例有多个重载,它们采用了不同类型的对象。

正如C++常见问题解答所说:尽可能使用会员,必要时使用朋友。

在某些情况下,最好将friend函数设置为自由函数,大多数情况与成员函数的第一个参数始终属于该类(其隐藏*this参数)有关。

一个例子是算术运算符重载:
假设您编写了一个表示复数的 complex 类。使用成员operator+()您可以编写表达式,例如 complex + float ,但不能编写float + complex 。但是你可以用operator+的自由形式来做到这一点:

class complex
{
    ...
    friend complex operator+( float f , complex c );
};

整个问题归结为"我为什么要在C++中使用朋友?答案是,如果使用得当,朋友会增强封装。这是一个常见问题解答:

朋友会违反封装吗?

当然,你的例子太短太抽象了。我能想到的一些更好的、现实生活中的例子涉及迭代器。您可能有许多迭代对象仅引用一个容器对象,并且您可能希望迭代器能够访问容器的私有成员变量。同时,您不希望容器向世界其他地方公开这些变量。

这样的设计可以通过friend功能完美实现。

许多人认为,制作访问器方法,您可以在开发的后期阶段设置障碍,阻止对成员变量的错误访问(甚至完全更改成员变量),而不会破坏(正确的)客户端。

一个经典案例是

class ComplexNumber {
  double real, imaginary;
public:
  double re() { return re; }
  double setRe(double v) { return re = v; }
// and so on ...
};
有一天,你

发现,在一些维护中,你需要那个数字的极坐标,所以你添加了方法

  double rho() { /* calculate rho */ }
  double theta() { /* calculate theta */ }
  double setRho(double v) { /* calculate real, imaginary, based on the new rho */ }

等等。

后来,你发现该类的用户对复数使用极坐标比笛卡尔坐标的频率要高得多,并且转换一直是性能问题的瓶颈,所以你放弃了realimaginary存储rhotheta,并更改了新的 - 更高效 - 存储的getter和setter方法,用于rhothetareim等等。类的所有客户端都可以毫无问题地重新编译,因为您更改了实现,但保持了接口稳定。