如何允许全局函数访问私有(private)成员?
限制是你不能直接friend
类声明中的全局函数。原因是因为我不希望用户必须在头文件中看到所有这些全局函数。函数本身是在实现文件中定义的,我希望尽可能将它们隐藏在那里。
现在您可能想知道为什么我有这么多这样的全局函数。为简单起见,我将各种 WNDPROC 函数与 windows 注册为回调,并且它们必须是全局的。此外,他们必须能够更新对不同类来说是私有(private)的信息。
我提出了 2 个解决方案,但都有些棘手。
解决方案1.制作所有需要后门的成员protected
而不是 private
.在实现文件中,声明一个继承自原始类但为 protected 成员提供公共(public) getter 的类更改器。当您需要 protected 成员时,您可以简单地转换为转换器类:
//Device.h
class Device{
protected:
std::map<int,int> somethingPrivate;
};
//Device.cpp
DeviceChanger : public Device{
private:
DeviceChanger(){} //these are not allowed to actually be constructed
public:
inline std::map<int,int>& getMap(){ return somethingPrivate; }
};
void foo(Device* pDevice){ ((DeviceChanger*)pDevice)->getMap(); }
当然,继承这个类的用户现在可以访问 protected 变量,但它至少允许我隐藏大部分重要的私有(private)变量,因为它们可以保持私有(private)。
这是有效的,因为
DeviceChanger
实例具有与 Device
完全相同的内存结构,所以没有任何段错误。当然,这正在蔓延到未定义的 C++ 域,因为该假设依赖于编译器,但我关心的所有编译器(MSVC 和 GCC)都不会改变每个实例的内存占用,除非添加了新的成员变量。解决方案 2. 在头文件中,声明一个友元转换器类。在实现文件中,定义那个 friend 类并使用它通过静态函数获取私有(private)成员。
//Device.h
class DeviceChanger;
class Device{
friend DeviceChanger;
private:
std::map<int,int> somethingPrivate;
};
//Device.cpp
class DeviceChanger{
public:
static inline std::map<int,int>& getMap(Device* pDevice){ return pDevice->somethingPrivate; }
};
void foo(Device* pDevice){ DeviceChanger::getMap(pDevice); }
虽然这确实为我的所有类添加了一个 friend (这很烦人),但只有一个 friend 可以将信息转发给任何需要它的全局函数。当然,用户可以简单地定义自己的
DeviceChanger
类,现在可以自由更改任何私有(private)变量。有没有更可接受的方式来实现我想要的?我意识到我正试图绕过 C++ 类保护,但我真的不想将每个需要访问其私有(private)成员的类中的每个全局函数都添加为好友;它在头文件中很丑陋,并且不容易添加/删除更多功能。
编辑:混合使用 Lake 和 Joel 的答案,我想出了一个完全符合我要求的想法,但是它使实现变得非常脏。基本上,您定义了一个具有各种公共(public)/私有(private)接口(interface)的类,但它的实际数据存储为指向结构的指针。该结构在 cpp 文件中定义,因此它的所有成员对该 cpp 文件中的任何内容都是公开的。即使用户定义了自己的版本,也只会使用实现文件中的版本。
//Device.h
struct _DeviceData;
class Device {
private:
_DeviceData* dd;
public:
//there are ways around needing this function, however including
//this makes the example far more simple.
//Users can't do anything with this because they don't know what a _DeviceData is.
_DeviceData& _getdd(){ return *dd; }
void api();
};
//Device.cpp
struct _DeviceData* { bool member; };
void foo(Device* pDevice){ pDevice->_getdd().member = true; }
这基本上意味着
Device
的每个实例除了指向某个数据 block 的指针外,它完全为空,但它为访问用户可以使用的数据提供了一个接口(interface)。当然,接口(interface)完全在cpp文件中实现。此外,这使得数据非常私密,甚至用户也看不到成员名称和类型,但您仍然可以在实现文件中自由使用它们。最后,您可以从
Device
继承并获得所有功能,因为实现文件中的构造函数将创建一个 _DeviceData
并将其分配给指针,它为您提供了所有 api()
力量。您必须更加小心移动/复制操作,以及内存泄漏。莱克给了我这个想法的基础,所以我相信他。谢谢你,先生!
最佳答案
我通常通过以抽象类的形式提取应用程序程序员接口(interface)来解决这个问题,抽象类是应用程序程序员(即您的库的用户)将能够使用的类型和操作的集合。
然后,在我的实现中,我声明 公众号其他类将在我的包中使用的所有方法和类型。
例如:
我以类似于以下方式定义 API 类:
class IDevice {
public:
// What the api user can do with the device
virtual void useMe() = 0;
};
然后,在我的库中(未暴露于用户界面):
class Device : public IDevice {
public:
void useMe(); // Implementation
void hiddenToUser(); // Method to use from other classes, but hidden to the user
}
然后,对于作为 API 一部分的每个 header (接口(interface)),我将使用 IDevice 类型而不是 Device 类型,并且当在内部我必须使用 Device 类时,我将指针向下转换到 Device。
假设您需要一个使用类 Device 的 Screen 类,但对用户完全隐藏(因此不会有任何 API 抽象类来实现):
#include "Device.h"
class Screen {
void doSomethingWithADevice( Device* device );
}
// Screen.cpp
void Screen::doSomethingWithADevice( Device* device ){
device->hiddenToUser();
}
这样,您不必仅仅因为您不希望用户看到/使用它而将其设为私有(private)。您获得了我称之为 API 的进一步抽象层(公共(public)之上的 1 个)。你将会拥有:
因此,您可以声明需要注册为回调方法的公共(public)方法,而用户不会看到它们。
最后,我将 API 的内容与二进制文件一起交付给用户,以便用户可以访问我在 API 中明确定义的内容,而没有其他内容。
关于c++ - 如何允许全局函数访问私有(private)成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18138509/