允许一个;“朋友”;类以仅访问某些私有成员 假设我有三个C++类FooA、FooB和Fooc。
FooA有一个名为允许一个;“朋友”;类以仅访问某些私有成员 假设我有三个C++类FooA、FooB和Fooc。,c++,friend,C++,Friend,FooA有一个名为Hello的成员函数,我想在FooB类中调用这个函数,但我不希望FooC类能够调用它。实现这一点的最好方法是将FooB声明为FooA的朋友类。但只要我这样做,所有的FooA的私人和受保护的成员都将被曝光,这是我无法接受的 所以,我想知道在C++(03或11)中是否有比friend类更好的机制可以解决这个难题 如果可以使用以下语法,我想这会很好: class FooA { private friend class FooB: void Hello(); void
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
}
};
没有什么可以让一个类成为一个特定函数的朋友,但是你可以让
FooB
成为一个带有私有构造函数的“key”类的朋友,然后让FooA::Hello
将该类作为一个被忽略的参数FooC
将无法提供参数,因此无法调用Hello
:
我想你可以在这里用
在你的例子中应该是这样的
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*/
}
};
不,这不是真正的限制。在我看来,限制首先存在于
friend
——一种破解设计缺陷的钝武器
您的班级FooA
不需要知道FooB
和FooC
以及“谁应该能够使用它”。它应该有一个公共接口,而不关心谁可以使用它。这就是界面的要点!在该接口内调用函数应始终使FooA
处于良好、安全、愉快、一致的状态
如果您担心您可能会在无意中使用
FooA
界面,那么请不要这样做;C++不是一种适合于保护这些类型的用户错误的语言。在这种情况下,您的测试覆盖率应该足够了
严格地说,我相信你可以通过一些极其复杂的“设计模式”获得你想要的功能,但是,老实说,我不想麻烦你
如果这是程序设计的语义问题,那么我礼貌地建议您的设计有缺陷。friend的整个思想就是向朋友公开您的类 有两种方法可以更具体地说明您公开的内容:
FooA
,这样只会公开受保护的和公共的方法最安全的解决方案是使用另一个类作为两个类的“中间人”,而不是让其中一个成为
朋友。
一种方法是@ForEveR在回答中建议的,但您也可以搜索代理类和其他可以应用的设计模式。您需要继承。试试这个:
// _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
无法访问私有的\u ClassA
方法。通过从接口类继承类的接口,可以将类的接口部分公开给指定的客户端
class FooA_for_FooB
{
public:
virtual void Hello() = 0;
virtual void Hello2() = 0;
};
class FooA : public FooA_for_FooB
{
private: /* make them private */
void Hello() override;
void Hello2() override;
private:
void Hello3();
int m_iData;
};
class FooB
{
void fun()
{
FooA objA;
FooA_for_FooB &r = objA;
r.Hello() // right
r.Hello2() // right
objA.Hello3() // compile error
objA.m_iData = 0; // compile error
}
};
class FooC
{
void fun()
{
FooA objA;
objA.Hello() // compile error
objA.Hello2() // compile error
objA.Hello3() // compile error
objA.m_iData = 0; // compile error
}
};
这里的访问控制是通过基类FooA\u for\u FooB
来增强的。通过类型为FooA\u for\u FooB
的引用,FooB
可以访问FooA\u for\u FooB
中定义的成员。但是,FooC
无法访问这些成员,因为它们在FooA
中已被重写为私有成员。您可以通过在FooC
中不使用类型FooA\u for\u FooB
或除FooB
以外的任何其他位置来实现此目的,这些位置可以在不太注意的情况下保留
这种方法不需要朋友,使事情变得简单
通过将基类中的所有内容都设置为私有,并在派生类中有选择地将某些成员包装并公开为公共成员,也可以实现类似的功能。不过,这种方法有时可能需要令人难堪的悲观情绪。(因为基类将成为整个程序中的“货币”)我最近不得不这么做,我不喜欢这些解决方案让类类型在当前名称空间中毫无目的地摇摆不定。如果您真的只希望这个功能可以用于单个类,那么我将使用与前面提到的不同的模式
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);
}
};
有人会说,与friend类相比,它遵循更好的OOP实践,因为它更好地封装了混乱的部分,并且不会将任何内容传递给继承的安全链。对于这种技术,您根本不需要修改安全类,从而使其更加模块化。这可能就是为什么许多较新的语言允许嵌套类,但几乎没有其他语言借用了friend概念的原因,尽管这只是添加了仅对单个类可用的功能(如果安全性被标记为final或其代码的重要部分被标记为private,则不起作用)。不,没有这样的事情,据我所知,谢谢!我在寻找它,但记不起它的名字:D“没有什么可以让一个类成为一个特定函数的朋友”听起来不对,顺便说一句。
friend void foo()
工作得很好,但我想你的意思是无法直接授予对其中一个函数的访问权。@Xeo我正确理解答案中的句子。如果没有friend void foo()
这个句子应该是“没有什么可以让一个特定函数成为类的朋友”。所以,对我来说,答案是正确的。我猜想Passkey模式是一个完美的解决方案,除了一件事:它污染了与一个不相关的参数的接口。如果“一个类”是C++中唯一的封装自然单元,这将是正确的。由于不是这样,这个答案有很多不准确之处。至少,朋友
很有趣
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);
}
};
#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
*/
}