当变量恰好是指针时,如何在非成员函数中使用私有成员变量

How can I use a private member variable in a non-member function, when the variable happens to be a pointer?

本文关键字:成员 变量 函数 指针      更新时间:2023-10-16

本质上,我的问题是我正在使用的库中的函数(本代码中的函数Foo)需要一个指向对象(object*mbar)的指针作为参数。然而,mbar是bar的私有成员变量。

通常,我只会使用getter并按值传递,但如果我传递指针,将直接访问资源,这将破坏封装。任何代码都可以调用getter并获得修改它的免费权限

接下来我想的是,我可以使用const指针,因为它们不允许修改它们指向的资源,但据我所知,我需要修改Foo才能接受它,这是不可能的,因为它是一个库函数。

我能想到的最后一件事就是简单地使用Bar的一个朋友来调用FoobarFunction,但我一直被告知朋友函数是最后的手段。

有没有一种方法可以在不破坏封装的情况下做到这一点?

//Main.cpp
#include "Foobar.h"
int main()
{
    Foobar fb;
    Bar b;
    fb.FoobarFunction(b);
    return 0;
}
//Bar.h
#include "Object.h"
class Bar
{
private:
    Object* mbar;
};
//Foobar.h
#include "Foo.h"
#include "Bar.h"
class Foobar
{
public:
    void FoobarFunction(Bar bar)
    {
        Foo(bar.mbar);
    }
};

简单的出路

您可以将指针设为常量,然后在将其传递给库函数时进行强制转换

Foo(const_cast<Object *>(bar.mbar));

如果Foo不尝试修改mbar,这将起作用。强制转换删除了"名义上的"常量。试图修改一个秘密的常量值可能会导致可怕的事情。

但是真的

即使有一种方法可以让Bar返回一个"只读"指针,问题中的代码示例仍然会违反封装。这种特殊的非封装风格被称为特征嫉妒:数据存在于一个对象中,但另一个对象正在进行大部分数据操作。一种更面向对象的方法是将操作和数据移动到同一个对象中。

很明显,你给我们的示例代码比你的实际项目复杂得多,所以我不知道重组代码的最明智的方法。这里有几个建议:

  1. 将FoobarFunction移动到条形:

    class Bar
    {
    private:
        Object* mbar;
    public:
        void FoobarFunction()
        {
            Foo(mbar);
        }
    };
    
  2. 使用依赖项注入。在创建Bar之前初始化mbar,然后将mbar传递到Bar的构造函数中。

    int main()
    {
        Object *mbar;
        Foobar fb;
        Bar b(mbar);
        fb.FoobarFunction(mbar);
        return 0;
    }
    

    在本例中,Bar不再是mbar的"所有者"。主要方法是直接创建毫巴,然后将其传递给任何需要它的人

    乍一看,这个例子似乎违反了我前面提到的准则(数据和行为存储在不同的对象中)。然而,以上和在Bar上创建getter之间有很大的区别。如果Bar有一个getMBar()方法,那么世界上任何人都可以来抓mbar,并将其用于他们想要的任何邪恶目的。但在上面的例子中,mbar(main)的所有者完全可以控制何时将其数据提供给另一个对象/函数。

除了C++之外,大多数面向对象的语言都没有"朋友"结构。根据我自己的经验,依赖注入是解决朋友们设计用来解决的许多问题的更好方法。

如果成员是私有的,那么它可能是私有的是有原因的。。。

如果Bar必须是Obj的唯一所有者,那么它不应该公开它,因为对Obj的任何其他更改都可能导致Bar的行为不正确。尽管如此,如果Bar不必是Obj的唯一所有者,您可以使用依赖项注入将getter放入Bar中,然后从外部将其传递到Bar中,这样以后也可以将其传递给foo

我认为你应该避免的一个解决方案是在Bar中调用foo。这可能违反单一责任原则

我认为,在这种情况下,你可以使用朋友的方法。我将向您介绍一个常见问题解答,声称朋友并不完全不适合封装。

不!如果使用得当,它们可以增强封装性。

当类的两半具有不同数量的实例或不同的生存期时,通常需要将类一分为二。在这些情况下,这两半通常需要直接访问对方(这两半过去在同一个类中,所以您没有增加需要直接访问数据结构的代码量;您只是将代码重新排列为两个类,而不是一个类)。实现这一点最安全的方法是让两半成为彼此的朋友。

如果你像刚才描述的那样使用朋友,你会把私人事情保密。不理解这一点的人往往会天真地努力避免在上述情况下使用友谊,而且他们往往会破坏封装。它们要么使用公共数据(怪诞!),要么通过公共get()和set()成员函数在两半之间访问数据。只有当私有数据"时,为私有数据具有公共get()和set()成员函数才是可以的;"有意义";从类外(从用户的角度)。在许多情况下,这些get()/set()成员函数几乎和公共数据一样糟糕:它们隐藏(仅)私有数据的名称,但不隐藏私有数据的存在。

类似地,如果使用友元函数作为类的公共访问函数的语法变体,则它们不会违反封装,就像成员函数违反封装一样。换句话说,类的朋友不会违反封装屏障:与类的成员函数一起,它们就是封装屏障。

(很多人认为友元函数是类外的东西。相反,试着把友元函数看作类公共接口的一部分。类声明中的友元函数并不违反封装,就像公共成员函数违反封装一样:两者在访问类的非公共部分方面具有完全相同的权限。)