基本上我的问题是我正在使用的库中的函数(在此代码中为函数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);
}
};
答案 0 :(得分:3)
您可以将指针设为const,然后在将其传递给库函数
时将其强制转换Foo(const_cast<Object *>(bar.mbar));
如果Foo不尝试修改mbar,这将有效。强制转换“仅在名称中”删除了常量。试图修改一个秘密const值可能导致可怕的事情。
即使有办法让Bar返回“只读”指针,你问题中的代码示例仍然会违反封装。这种特殊的非封装风格称为feature envy:数据存在于一个对象中,但另一个对象正在进行大部分数据操作。更面向对象的方法是将操作和数据移动到同一个对象中。
显然,您提供给我们的示例代码比实际项目简单得多,因此我无法知道重构代码的最合理方法。以下是一些建议:
将FoobarFunction移动到Bar:
class Bar
{
private:
Object* mbar;
public:
void FoobarFunction()
{
Foo(mbar);
}
};
使用dependency injection。在创建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 ++之外,大多数面向对象的语言都没有“朋友”构造。根据我自己的经验,依赖注入是解决朋友设计解决的许多问题的更好方法。
答案 1 :(得分:2)
如果该成员是私人成员,可能因为某种原因而私有......
如果Bar必须是Obj的唯一所有者,那么它不应该暴露它,因为对Obj的任何其他更改可能会导致Bar操作不正确。
虽然,如果Bar不必是Obj的唯一所有者,你可以使用getter使用依赖注入并从外部将其传递到Bar,这样你以后也可以将它传递给foo
。
我认为你应该避免的解决方案就是在Bar里面调用foo。这可能违反了Single Responsibility Principle
我认为在这种情况下很难,你可以使用朋友的方法。 我会推荐你FAQ声称朋友并不总是对封装不利。
没有!如果使用得当,它们会增强封装效果。
当两个部分具有不同数量的实例或不同的生命周期时,您经常需要将一个类分成两半。在这些情况下,这两个半部分通常需要直接相互访问(这两个部分曾经在同一个类中,所以你没有增加需要直接访问数据结构的代码量;你只需要重新调整代码分为两类而不是一类)。实现这一目标的最安全的方法是让两半成为彼此的朋友。
如果您使用刚才描述的朋友,您会将私人物品保密。不理解这种情况的人经常做出天真的努力以避免在上述情况下使用友谊,并且通常他们实际上破坏了封装。它们要么使用公共数据(怪诞!),要么通过公共get()和set()成员函数使数据在两半之间可访问。只有当私有数据从类外部“有意义”时(从用户的角度来看),拥有私有数据的公共get()和set()成员函数才是正常的。在许多情况下,这些get()/ set()成员函数几乎与公共数据一样糟糕:它们隐藏(仅)私有数据的名称,但它们并不隐藏私有数据的存在。
类似地,如果您使用友元函数作为类的公共访问函数的语法变体,它们不会违反封装,而不是成员函数违反封装。换句话说,一个类的朋友不会违反封装障碍:与类的成员函数一起,它们是封装障碍。
(许多人认为朋友的功能是课外的东西。相反,尝试将朋友的功能视为类的公共接口的一部分。类声明中的友元函数不会违反封装而不是公开成员函数违反封装:两者在访问类的非公共部分方面具有完全相同的权限。)