假设我有三个C ++类FooA,FooB和FooC。
FooA有一个名为Hello
的成员函数,我想在类FooB中调用此函数,但我不希望类FooC能够调用它。我能想出来实现这一点的最好方法是将FooB声明为FooA的朋友类。但只要我这样做,所有 FooA的私人和受保护成员都将被曝光,这对我来说是非常不可接受的。
所以,我想知道C ++(03或11)中是否有任何机制比friend
类更能解决这个难题。
我认为如果可以使用以下语法会很好:
class FooA
{
private friend class FooB:
void Hello();
void Hello2();
private:
void Hello3();
int m_iData;
};
class FooB
{
void fun()
{
FooA objA;
objA.Hello() // right
objA.Hello2() // right
objA.Hello3() // compile error
ojbA.m_iData = 0; // compile error
}
};
class FooC
{
void fun()
{
FooA objA;
objA.Hello() // compile error
objA.Hello2() // compile error
objA.Hello3() // compile error
ojbA.m_iData = 0; // compile error
}
};
答案 0 :(得分:29)
没有什么可以让一个类成为一个特定函数的朋友,但你可以使用私有构造函数使FooB
成为“key”类的朋友,然后让FooA::Hello
将该类作为一个忽略参数。 FooC
将无法提供参数,因此无法调用Hello
:
Is this key-oriented access-protection pattern a known idiom?
答案 1 :(得分:22)
我认为你可以在这里使用Attorney-Client。
在你的情况下,例子应该是这样的
class FooA
{
private:
void Hello();
void Hello2();
void Hello3();
int m_iData;
friend class Client;
};
class Client
{
private:
static void Hello(FooA& obj)
{
obj.Hello();
}
static void Hello2(FooA& obj)
{
obj.Hello2();
}
friend class FooB;
};
class FooB
{
void fun()
{
FooA objA;
Client::Hello(objA); // right
Client::Hello2(objA); // right
//objA.Hello3() // compile error
//ojbA.m_iData = 0; // compile error
}
};
class FooC
{
void fun()
{
/*FooA objA;
objA.Hello() // compile error
objA.Hello2() // compile error
objA.Hello3() // compile error
ojbA.m_iData = 0; // compile error*/
}
};
答案 2 :(得分:4)
不,这不是一个真正的限制。在我看来,限制是friend
- 一种用于破解设计缺陷的直接武器 - 首先存在。
您的班级FooA
无法了解FooB
和FooC
以及“哪一个应该能够使用它”。它应该有一个公共接口,并且不关心谁可以使用它。这就是界面的重点!在该界面中调用函数应始终使FooA
保持良好,安全,快乐,一致的状态。
如果您担心的是,您可能会意外地从某个您不想要的地方使用FooA
界面,那么,根本就不要这样做; C ++不是一种适合防范这类用户错误的语言。在这种情况下,您的测试覆盖率就足够了。
严格地说,我确信你可以通过一些可怕的复杂“设计模式”来获得你所拥有的功能,但老实说,我不会打扰。
如果这是程序设计语义的问题,那么我礼貌地建议你的设计存在缺陷。
答案 3 :(得分:1)
最安全的解决方案是使用另一个类作为两个类的“中间人”,而不是将其中一个作为friend.
@ForEveR在答案中提出了一种方法,但是您还可以搜索可以应用的代理类和其他设计模式。
答案 4 :(得分:1)
通过从接口类继承类,可以将类的接口部分公开给指定的客户端。
FooA_for_FooB
此处,基类FooA_for_FooB
增强了访问控制。通过FooB
类型的引用,FooA_for_FooB
可以访问FooC
中定义的成员。但是,FooA
无法访问这些成员,因为它们已被FooA_for_FooB
中的私有成员覆盖。您可以通过不使用FooC
内的FooB
类型或friend
以外的任何其他地方来实现您的目的,这些地方可以保留而无需太多关注。
这种方法不需要java.io.FileNotFoundException: C:\Users\112419442\Desktop\BootStrapLogin\sql\createdb.txt
,简单易行。
类似的事情可以通过在基类中使所有内容成为私有来完成,并在派生类中有选择地将一些成员包装并公开为public。不过,这种方法有时可能需要丑陋。 (因为基类将成为整个程序中的“货币”。)
答案 5 :(得分:0)
friend
的整个想法是将你的课程暴露给朋友。
有两种方法可以更具体地说明您所揭露的内容:
继承自FooA
,这样只会暴露受保护和公开的方法。
只与某种方法交朋友,只有这种方法才有权访问:
friend void FooB::fun();
答案 6 :(得分:0)
你需要继承。试试这个:
// _ClassA.h
class _ClassA
{
friend class ClassA;
private:
//all your private methods here, accessible only from ClassA and _ClassA.
}
// ClassA.h
class ClassA: _ClassA
{
friend class ClassB;
private:
//all_your_methods
}
这样你就有:
ClassB
是唯一能够使用ClassA
的人。
ClassB
无法访问私有的_ClassA
方法。
答案 7 :(得分:-1)
我最近不得不这样做,我不喜欢这些解决方案让类类型在当前命名空间中徘徊而基本上没有任何目的的方式。如果您真的只希望此功能可用于单个类,那么我将使用与上述不同的模式。
class Safety {
protected:
std::string _Text="";
public:
Safety(const std::string& initial_text) {
_Text=initial_text;
}
void Print(const std::string& test) {
std::cout<<test<<" Value: "<<_Text<<std::endl;
}
};
class SafetyManager {
protected:
// Use a nested class to provide any additional functionality to
// Safety that you want with protected level access. By declaring
// it here this code only belongs to this class. Also, this method
// doesn't require Safety to inherit from anything so you're only
// adding weight for the functionality you need when you need it.
// You need to be careful about how this class handles this object
// since it is really a Safety cast to a _Safety. You can't really
// add member data to this class but static data is ok.
class _Safety : Safety {
public:
void SetSafetyText(const std::string& new_text) {
_Text=std::string(new_text);
}
};
public:
static void SetSafetyText(Safety* obj, const std::string& new_text) {
if(obj==nullptr) throw "Bad pointer.";
_Safety& iobj=*(_Safety*)obj;
iobj.SetSafetyText(new_text);
}
};
然后在 main(或其他任何地方)中,您不能通过 Safety 修改 _Text,但可以通过 SafetyManager(或其后代)。
#include "Safety.h"
int main() {
Safety t("Hello World!");
t.Print("Initial");
SafetyManager::SetSafetyText(&t, "Brave New World!");
t.Print("Modified");
/*
t._Text; // not accessible
Safety::SetSafetyText(&t, "ERR");// doesn't exist
t.SetSafetyText(&t, "ERR"); // doesn't exist
_Safety _safety; // not accessible
SafetyManager::_Safety _safety; // not accessible
*/
}
有人会说这比友元类遵循更好的 OOP 实践,因为它更好地封装了混乱的部分,并且不会在继承的安全链中传递任何东西。您也根本不需要为这种技术修改 Safety 类,使其更加模块化。这些可能是许多较新的语言允许嵌套类的原因,但几乎没有其他语言借鉴了友元概念,尽管这只是添加了仅对单个类可用的功能(并且在以下情况下不起作用)安全被标记为最终或将其代码的重要部分标记为私有)。