5

本质上我的问题是我正在使用的库中的一个函数(此代码中的函数 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);
    }
};
4

2 回答 2

3

简单的出路

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

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

如果 Foo 不尝试修改 mbar,这将起作用。演员表“仅在名义上”删除了常量。试图修改一个秘密的常量值可能会导致可怕的事情。

不过真的...

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

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

  1. 将 FoobarFunction 移入 Bar:

    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 的“所有者”。main 方法直接创建 mbar,然后将其传递给需要它的任何人。

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

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

于 2014-04-27T08:35:28.913 回答
2

如果该成员是私人的,它可能是私人的原因......

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

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

我相信在这种情况下很难,你可以使用朋友的方法。我将向您推荐一个FAQ,声称朋友并不总是不适合封装。

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

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

如果您使用刚刚描述的朋友,您将保持私密的私密性。不理解这一点的人通常会天真地努力避免在上述情况下使用友谊,并且通常他们实际上破坏了封装。它们要么使用公共数据(怪诞!),要么通过公共 get() 和 set() 成员函数使数据在两半之间可访问。只有当私有数据从类外部(从用户的角度)“有意义”时,才能为私有数据使用公共 get() 和 set() 成员函数。在许多情况下,这些 get()/set() 成员函数几乎与公共数据一样糟糕:它们(仅)隐藏了私有数据的名称,但它们不隐藏私有数据的存在。

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

(许多人认为友元函数是类之外的东西。相反,尝试将友元函数视为类的公共接口的一部分。类声明中的友元函数不会违反封装,正如公共成员函数违反封装:两者在访问类的非公共部分方面具有完全相同的权限。)

于 2014-04-27T07:25:46.810 回答