允许"friend"类仅访问某些私有成员
Allowing a "friend" class to access only some private members
假设我有三个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
}
};
我认为您可以在这里使用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*/
}
};
没有什么可以让一个类成为一个特定函数的朋友,但你可以用私有构造函数让FooB
成为一个"键"类的朋友,然后让FooA::Hello
将该类作为一个被忽略的参数。FooC
将无法提供参数,因此无法调用Hello
:
这种面向密钥的访问保护模式是众所周知的习惯用法吗?
您可以通过从接口类继承类的接口,将类的接口部分公开给指定的客户端。
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_for_FooB
来增强访问控制。通过FooA_for_FooB
类型的引用,FooB
可以访问在FooA_for_FooB
中定义的成员。但是,FooC
无法访问这些成员,因为它们在FooA
中已被覆盖为私有成员。您的目的可以通过不使用FooC
中的类型FooA_for_FooB
或除FooB
之外的任何其他地方来实现,这些地方可以不用太多注意就可以保留。
这种方法不需要friend
,使事情变得简单。
类似的事情可以通过使基类中的所有内容都是私有的,并选择性地将派生类中的一些成员包装和公开为公共成员来完成。不过,这种方法有时可能需要丑陋的悲观情绪。(因为基类将成为整个程序中的"货币"。(
否,这并不是真正的限制。在我看来,局限性在于friend
——一种针对设计缺陷进行黑客攻击的钝器——首先存在。
您的类FooA
不需要知道FooB
和FooC
以及"哪一个应该能够使用它"。它应该有一个公共接口,不在乎谁可以使用它。这就是接口的重点!调用该接口中的函数应始终使FooA
处于良好、安全、愉快、一致的状态。
如果你担心你可能无意中从某个地方使用了FooA
接口,那么,不要这么做;C++不是一种适合防止此类用户错误的语言。在这种情况下,您的测试覆盖率应该足够了。
严格地说,我相信你可以通过一些极其复杂的"设计模式"获得你想要的功能,但老实说,我不会介意。
如果这是程序设计的语义问题,那么我礼貌地建议您的设计有缺陷。
最安全的解决方案是使用另一个类作为两个类的"中间人",而不是将其中一个作为friend.
。@ForEverR在回答中建议了一种方法,但您也可以搜索代理类和其他可应用的设计模式。
您需要继承。试试这个:
// _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
}
通过这种方式,您可以:CCD_ 24是唯一能够使用CCD_。ClassB
无法访问私有的_ClassA
方法。
您可以在基类中隐藏私有成员,然后使FooA成为该基类的子级和朋友。
// allows us to hide private members from friends of FooA,
// but still allows FooA itself to access them.
class PrivateFooA
{
private:
friend class FooA;
// only allow FooA to derive from this class
PrivateFooA() {};
// hidden from friends of FooA
void Hello3();
int m_iData;
};
// this class hides some of its private members from friend classes
class FooA : public PrivateFooA
{
private:
// give FooB access to private methods
friend class FooB;
void Hello();
void Hello2();
};
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隐藏的任何东西都可以放入PrivateFooA(必须是私人成员(,其他所有东西都可以直接放入FooA。FooA将能够访问PrivateFooA中的所有内容,就像它自己的成员一样。
这更多的是对user3737631答案的扩展,但我认为它值得发布,因为它包括了OP中的类、PrivateFooA中的私有构造函数,以及一些我认为会有所帮助的其他注释。
friend
的整个想法是将您的类公开给朋友。
有两种方法可以更具体地说明你暴露的内容:
-
从
FooA
继承,这样只公开受保护的和公共的方法。 -
只与某个方法交朋友,这样只有该方法才能访问:
friend void FooB::fun();
我最近不得不这样做,我不喜欢这些解决方案将类类型挂在当前命名空间中而没有任何目的的方式。如果你真的只是想让这个功能对一个类可用,那么我会使用一个不同于上面提到的模式。
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实践,因为它更好地封装了混乱的部分,并且不会将任何东西传递到继承的安全链中。您也根本不需要为这种技术修改安全类,使其更加模块化。这可能就是为什么许多较新的语言允许嵌套类,但几乎没有其他语言借用了友元概念的原因,尽管只是添加了仅对单个类可用的功能(如果安全性标记为最终或将其代码的重要部分标记为私有,则不起作用(。
- 这是关于成员访问规则的正确摘要吗
- 为什么我在空指针错误(链表)中获取成员访问权限
- 成员访问是否在空指针上定义C++?
- C++ IDE 不会推断/自动完成对模板类中的 std::array 下标表达式的成员访问
- 为什么类成员数据必须是静态的才能被模板化类的模板化结构成员访问
- 为什么c++允许成员函数定义中实例的私有成员访问
- C/C++ 包含点的宏参数(成员访问运算符)
- 访问说明符(私有/公共/受保护)如何在内部工作(限制成员访问)?
- 如何将超类的受保护成员访问到其派生类. 如果已在派生类中声明了具有相同名称的函数?
- 内部类私有成员访问和封闭的友好性
- 通过 C++ 中的另一个结构成员访问结构
- 具体化 PRVALUES 成员访问的 decltype 行为不正确
- 常量表达式中的静态成员访问
- XVALUE来自类成员访问表达式
- 未经授权的私有类成员访问会产生编译时错误而不是运行时错误?
- 在 c++ 中,为什么 -> 被称为二进制中缀指针成员访问运算符?
- 如何访问模板参数的成员?“成员访问不完整的类型”
- 不明确的可变参数类成员访问
- C 受保护的成员访问
- 将typeID转换为静态成员访问(C )的命名空间