设计模式以限制对C++类的公共成员的访问


为了简洁起见,下面的所有代码都是伪代码。

考虑一个事件系统,其中具有以下类:

class Event_dispatcher {
private:
Listener_collection listeners;
public:
void add_listener(Listener _listener) { ... }
void remove_listener(Listener _listener) { ... }
void post_event(Event _event) { ... }
};

我打算在类中拥有Event_dispatcher的实例,这些实例旨在发送事件。但是,我只希望从类内部发布事件,这样用户就只能添加或删除侦听器,而不能发布事件
简单的解决方案是使Event_dispatcher成为封闭类的私有成员,但随后,我必须将add_listenerremove_listener定义为将参数转发给事件调度程序的公共成员
我知道这两种方法似乎不多,但请记住,这只是一个例子,我故意缩短了它。想象一下同样的问题,我必须以类似的方式定义大量的方法。

我想知道是否有一种常见或推荐的方法来解决这样的问题。我想到的一个解决方案是定义这样一个接口类:

class Event_dispatcher_interface {
private:
Event_dispatcher* event_dispatcher;
public:
void add_listener(Listener _listener) { event_dispatcher->add_listener(_listener); }
void remove_listener(Listener _listener) { event_dispatcher->remove_listener(_listener); }
};

并使其成为封闭类的公共成员。内部的指针(*event_dispatcher(将指向封闭类的私有成员Event_dispatcher,以便只允许从类内部发布事件。

如果还有其他(更好的(解决方案,我将不胜感激。

您可以使用继承来继承可见性

class Event_dispatcher {
private:
Listener_collection listeners;
public:
void add_listener(Listener _listener) { ... }
void remove_listener(Listener _listener) { ... }
protected:
void post_event(Event _event) { ... }
};

然后

struct MyClass : Event_dispatcher
{
// accessible as public
// void add_listener(Listener _listener);
// void remove_listener(Listener _listener);
// only usage from within the class
//void post_event(Event _event);
};

此方法依赖于委派友谊

您可以将所有Event_dispatcher成员函数设置为私有函数,并将大友谊设置为访问这些成员函数的单独类:

class Event_dispatcher {
friend class Listener_manager;
friend class Event_poster;
// all private
void add_listener(Listener _listener) { /* */ }
void remove_listener(Listener _listener) { /* */ }
void post_event(Event _event) { /* */ }
Listener_collection listeners;
};

然后,定义分别提供对add_listener()/remove_listener()post_event()的访问的Listener_managerEvent_poster类。它们都依赖委派:

struct Listener_manager {
Listener_manager(Event_dispatcher& ed): dispatcher(ed) {}   
void add_listener(Listener _listener) {
dispatcher.add_listener(_listener);
}
void remove_listener(Listener _listener) {
dispatcher.remove_listener(_listener);
}
private:
Event_dispatcher& dispatcher;
};
struct Event_poster {
Event_poster(Event_dispatcher& ed): dispatcher(ed) {}
void post_event(Event _event) { dispatcher.post_event(_event); }
private:
Event_dispatcher& dispatcher;
};

在类中,将这些类的对象声明为数据成员。您可以单独控制每个成员的访问权限:

struct MyClass {
public:
MyClass(): listener(dispatcher), poster(dispatcher) {}
private:
Event_dispatcher dispatcher;
public: // accessible from the outside
Listener_manager listener_manager; // add_listener()/remove_listener()
private:
Event_poster event_poster; // post_event()
};

您可以将add_listener()remove_listener()称为:

MyClass obj;
obj.listener.add_listener(listener);
obj.listener.remove_listener(listener);

由于成员event_poster是私有的,因此其成员函数post_event()只能在MyClass内部访问。


使用CRTP

相反,您可以保留Event_dispatcher类成员的原始可访问性,并定义以下类模板Accessor<>,它将处理委派部分:

template<typename T>
class Accessor {
public:
void add_listener(Listener _listener) {
static_cast<T&>(*this).event_dispatcher.add_listener(_listener);
}
void remove_listener(Listener _listener) {
static_cast<T&>(*this).event_dispatcher.remove_listener(_listener);
}
};

然后,从这个在定义类本身上实例化的类模板公开继承(即CRTP(:

struct MyClass: Accessor<MyClass> {
friend class Accessor<MyClass>;
private:
Event_dispatcher event_dispatcher;
};

您仍然需要将友谊授予基类,以便它可以访问私有数据成员event_dispatcher,并且该成员必须具有此名称。

通过这种方式,您可以以更简洁的方式访问Event_dispatcher::add_listener()Event_dispatcher::remove_listener()

MyClass obj;   
obj.add_listener(listener);
obj.remove_listener(listener);

我相信您正在寻找的是朋友类:

cplusplus.com在这个上有一个很好的页面

TLDR
当您将类B指定为类a的朋友时,类B可以完全访问类a的私有功能。

试试这个:

class Outer_Class {
public:
Event_dispatcher my_event_dispatcher;
// whatever else is public

private:
void example_func();
};
class Event_dispatcher {
friend class Outer_Class;
private:
Listener_collection listeners;
void post_event(Event _event) { ... }
public:
void add_listener(Listener _listener) { ... }
void remove_listener(Listener _listener) { ... }
};
void Outer_Class::example_func(){
// In here, you can use the functions from the class that made it a friend
my_event_dispatcher.post_event(my_event);
}

最新更新