c++ - 如何允许全局函数访问私有(private)成员

标签 c++

如何允许全局函数访问私有(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:IDevice.h
  • 内部:Device.h Device.cpp

  • 我以类似于以下方式定义 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 个)。你将会拥有:
  • API//方法/类型对应用程序员可见
  • public//方法/类型对整个库包可见,但对 api 用户不可见
  • protected//方法/类型仅对定义它的类的子类可见
  • private//定义类的本地方法/类型

  • 因此,您可以声明需要注册为回调方法的公共(public)方法,而用户不会看到它们。

    最后,我将 API 的内容与二进制文件一起交付给用户,以便用户可以访问我在 API 中明确定义的内容,而没有其他内容。

    关于c++ - 如何允许全局函数访问私有(private)成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18138509/

    相关文章:

    c++ - #include<vector> 没有那个文件或目录 2

    C++ 扫雷器 AjacentMines

    c++ - Sqlite C++ 图像到 Blob 不会存储任何内容

    c++ - Windows Vista/7 上的 SDL_Mixer MIDI 音量问题

    c++ - 以编程方式在 MFC 中设置单选按钮状态

    c++ - 通过默认字符串简化 vector 中字符串的初始化

    c++ - 包含另一个类的对象的类的大小

    c++ - 相同的代码,相同的库版本,相同的编译器,不同的输出

    带指针的 C++ STL 容器 : A few questions

    c++ - const 重载的 operator[] 函数及其调用