繁体   English   中英

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

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

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

简单的出路

您可以将指针设为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上创建吸气剂之间存在很大差异。 如果Bar具有getMBar()方法,那么世界上任何人都可以出现并抓住mbar并将其用于他们想要的任何邪恶目的。 但是在上面的示例中,mbar(主)的所有者完全控制了何时将其数据提供给另一个对象/功能。

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

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

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

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

我相信在这种情况下您可以使用朋友方法。 我将向您介绍一个FAQ ,该FAQ声称朋友对于封装并不总是不好的。

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

当两个半部分具有不同数量的实例或不同的生存期时,通常需要将一个类分成两半。 在这些情况下,这两个半部通常需要彼此直接访问(这两个半部以前属于同一类,因此您并没有增加需要直接访问数据结构的代码量;只是简单地改组了代码分为两类,而不是一类)。 实现此目的最安全的方法是使两半成为朋友。

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

同样,如果将朋友函数用作类的公共访问函数的语法变体,则它们不会违反封装,而成员函数不会违反封装。 换句话说,类的朋友不会违反封装障碍:与类的成员函数一起,它们是封装障碍。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM