【问题标题】:What is the best C++ design to contain a class's objects?包含类对象的最佳 C++ 设计是什么?
【发布时间】:2015-02-01 01:54:41
【问题描述】:

我有一个包含许多对象的类,我想将它们分组到某种类型的容器中,并使用某种类型的标识符访问它们。

class Menu {
Object title;
Object play;
Object instructions;
Object pause;
...
};

在类中列出每个对象,如上所示,这很好,因为我可以像menu.title 一样访问它们,但是我必须重新键入每个名称才能将其添加到容器中,vector.push_back(title)

下面显示的是我一直解决问题的方法。我使用枚举的整数值来访问相应的索引。 objects[TITLE]objects[PLAY]

class Menu {
std::vector<Object> objects;
};

enum ObjectTypes {
TITLE, PLAY, INSTRUCTIONS, PAUSE, ...
};

我通常不喜欢这种方法,因为它看起来是间接的,并且在使用嵌套类和枚举时,标识符可能会变得冗长且繁琐。我来自 C 语言,对 C++ 有点陌生。有没有人有更实用和/或优雅的方法来解决这个问题?欢迎使用 C++11 及更高版本!

【问题讨论】:

  • 你考虑过std::map吗?它是一个关联容器,这意味着您可以使用menu["title"] 引用对象,其中"title"std::string
  • 您可以创建在众所周知的位置引用的方法,或者如果它是一个数组,那么在构造时绑定它们会很简单。
  • 它也可以是一个 std::map.
  • 我不想使用映射,因为这样我就有机会拼错字符串,或者我仍然需要一个单独的字符串列表或标识符容器,例如枚举。跨度>
  • @Barry: 它不一定是向量,但我想对每个对象应用一些函数

标签: c++ class oop object vector


【解决方案1】:

您使用的方法很好。如果你想避免繁琐的标识符,你可以做一个临时引用来保持更简洁。例如,而不是调用:

menu.objects[PAUSE].foo();
menu.objects[PAUSE].bar();
menu.objects[PAUSE].baz();

...您可以在必要时这样做:

Object & pause = menu.objects[PAUSE];
pause.foo();
pause.bar();
pause.baz();

它的工作原理相同,但没有所有冗余字符。

【讨论】:

    【解决方案2】:

    我认为你的方法很好。使用 std::map&lt;ObjectType, Object&gt; 而不是 std::vector 可能更安全一些。

    无论哪种方式,如果您想节省一点打字时间,您可以在Menu 上重载operator[]

    Object& operator[](index_type i){ return objects[i]; }
    const Object& operator[](index_type i) const { return objects[i]; }  
    

    Then you can writemenu[TITLE].

    除此之外,我看不出它怎么能不那么麻烦,那里没有多余的信息,正如 Jeremy 指出的那样,如果您多次需要一个对象,您总是可以创建一个本地引用 auto&amp; title = menu[TITLE]; .

    根据Menu 的其他职责,也许您根本不需要Menu 类,您可以直接使用mapvector

    【讨论】:

      【解决方案3】:

      您问题的最佳解决方案主要取决于您的用例。 我看到两个主要用例:

      1. 您希望表示“设备”的“功能”,以便在从代码中操作该“设备”时获得可读的代码。例如具有播放、停止、暂停操作的 MediaPlayer 设备。但是您取消了简单地将成员函数添加到您的“设备”对象的选项,例如 Play(),因为您想将您的播放代码也重新用于另一个设备,例如调谐器设备。此外,您希望将操作应用于所有或这些“函数”的子集,例如 Enable()/Disable()/ToString()/Trigger(),Configure()...,这就是成员函数的原因方法不利。
      2. 您想要创建一个文档对象模型,该模型更注重数据。例如 Xml 文档。

      根据您在问题中所写的内容,我假设您已经想到了用例 1。您的Object 类型具有您需要的所有常用操作。
      然而,所有这些“功能”之间存在差异。

      为了坚持您的简单方法,您需要手动设置/配置您的 Object 实例,从长远来看,这可能会很烦人,但很难避免:

      // Example of "annoying": If you have more than 1 "device", 
      // you have to write such a function for each of them.
      // Also, there is that fishy bit with Object.Id - the association between
      // your configuration data and your object you want to configure.
      void ConfigureMenu( std::vector& menuItems, MenuConfigurationData& configData )
      {
          for( auto& menuItem : menuItems )
          {
               menuItem.Configure( configData[menuItem.Id] ); // spoiler!
          }
      }
      

      另外,我倾向于认为即使现在您的问题中也没有显示一些代码,这些代码配置了您的对象。

      考虑到这一点,您可能希望摆脱手动为每个“设备”编写 1 个类类型的想法。下一个设备/菜单将需要同样对待,但需要更多专门的编码。

      所以,我对你的建议是永远摆脱你的class Menu,抽象你的问题并为你的问题建模,就像:对象是你的“功能”,设备只是一组功能/对象.然后,您的 class Menu 就变成了一个名为 Menu 的实例。

      typedef uint32_t FunctionId; // for example uint32_t...
      typedef std::map<FunctionId,Object> Device; // aka. Menu.
      

      然后,无论如何,在您最有可能拥有的配置函数中,您传入该 Device 映射的实例,并且您的配置函数用正确配置的 Object 填充它。

      // those enums are still specific to your concrete device (here Menu) but 
      // you can consider the time it takes writing them an investment which will
      // pay off later when you write your code, using your functions.
      // You assign the function id which is used in your meta-data.
      enum class MenuFunctions : FunctionId { play = ..., title = ..., instructions, ... };
      
      // "generic" configuration function.
      // Does not only configure your Object, but also puts it inside the device.
      void ConfigureDevice( Device& device, ConfigData& configData )
      {  // ...
      }
      

      稍后在您的代码中,您可以像这样访问函数:

      menu[MenuFunctions::play].Trigger();
      

      当然,还有其他方法和变体。例如,假设您有元数据(配置数据、设备描述),您可以停止手动编写所有这些代码,而是编写一些代码生成器来为您完成这项工作。
      这种生成器的第一个版本可以为您创建配置函数和枚举。

      有了所有这些,“嵌套类”的用例就变成了创建设备实例集合的问题。现在,由于您的所有设备都属于同一类型,您可以在闲暇时对它们进行组合和分组。

      【讨论】:

        【解决方案4】:

        以下几种不同的方法。我不是说哪个是“最好的”。它们都有优点/缺点。

        A) 当你的元素数量不变时,不要使用向量(例如class Menu { std::vector&lt;Object&gt; objects; };),而是使用数组class Menu {std::array&lt;Object,NObjectTypes&gt; objects;};

        B) 只需使用一个类,但提供一个 api 来返回对您的对象的引用的std::array&lt;&gt;

        class Menu {
         Object title;
         Object play;
         Object instructions;
         Object pause;
        ...
         std::array<Object*,NObjects> allObjects();
        };
        

        C) std::tuple 在您的类型不完全相同时会很有用。


        对于菜单,我通常会选择“A”。

        【讨论】:

          猜你喜欢
          • 2021-10-23
          • 1970-01-01
          • 2011-06-18
          • 2023-03-11
          • 2022-01-25
          • 2018-05-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多