【问题标题】:Create and dynamically allocate multiple versions of a class创建并动态分配一个类的多个版本
【发布时间】:2021-09-28 21:01:47
【问题描述】:

由于我正在处理的项目的硬件版本不同,有两个版本的 c++ 类控制硬件。应该使用的类的版本应该在运行时确定,因此实例化需要动态发生。两个类的函数名和返回类型总是相同的。

我应该如何创建这样一个类?我已经阅读了多态性和类继承,并尝试编写以下代码,但它不起作用。我觉得我正在朝着正确的方向前进,但缺乏完全解决这个问题的经验。我下面的实现不会动态创建类,我认为这是导致我的问题的原因。

提前感谢您的帮助。

#include <string>
#include <iostream>

class HWVer1                     { 
    private: 
        std::string _msg; 
    public:
        HWVer1(std::string x): _msg(x) {} 
        void do_something(){ std::cout<<"hello from HWVer1: "<<_msg <<"\n"; } 
}; 

class HWVer2                     { 
    private: 
        std::string _msg; 
    public:
        HWVer2(std::string x): _msg(x) {} 
        void do_something(){ std::cout<<"hello from HWVer2: "<<_msg <<"\n"; } 
}; 

class Wrapper1: public         HWVer1 { public: Wrapper1(std::string x):HWVer1(x){}  }; 
class Wrapper2: public         HWVer2 { public: Wrapper2(std::string x):HWVer2(x){}  }; 



int main(int argc, char ** argv){
    
    int HW_ver = 2;
    
    if (HW_ver == 1){
        Wrapper1 HW("Wrap1");
    }
    
    else if (HW_ver == 2){
        Wrapper2 HW("Wrap2"); 
    }

    HW.do_something();  //  error: ‘HW’ was not declared in this scope --> does not work because reference to HW is destroyed after scope of if /else if statement ends 

}

注意:HW.do_something() 不在 if 语句中的原因是因为在 HWVer1/HWVer2 中找到的函数在整个代码的许多地方都使用过。这个练习的重点是避免在每次访问硬件类之前使用 if/else if 语句。我只是想改变一点开销,让其余的代码保持原样。为此,声明的硬件类需要可全局访问,因为这是当前实现硬件类的各个版本的方式。

更新:

我已经解决了我的问题。以下似乎有效:


#include <string>
#include <iostream>


class HWVerBase
{ 
    public:
        virtual void do_something() = 0;
        virtual void do_something_else() = 0;
}; 

class HWVer1: public HWVerBase
{
    public:
        void do_something(void){
            std::cout << "I am in HWVer1!" << std::endl;
        }
        void do_something_else(void){
            std::cout << "I am doing something else in HWVer1!" << std::endl;
        }
};

class HWVer2: public HWVerBase
{
    public:
        void do_something(void){
            std::cout << "I am in HWVer2!" << std::endl;
        }
        void do_something_else(void){
            std::cout << "I am doing something else in HWVer2!" << std::endl;
        }
};


int main(int argc, char ** argv){
    
    int HW_ver = 1;
    
    HWVerBase* ptrHWVer;
    
    if (HW_ver == 1){
        ptrHWVer = new HWVer1;
    }
    
    else if (HW_ver == 2){
        ptrHWVer = new HWVer2;
    }    
    
    ptrHWVer->do_something();
    ptrHWVer->do_something_else();

    delete ptrHWVer;    

}

输出:

我在HWVer1! 我正在 HWVer1 中做其他事情!

这种方法的唯一“缺点”是所有定义为虚拟的函数也必须在 HWVer1 和 HWVer2 类中定义。我认为如果能够按照 MSalters 下面的建议使用 std::shared_ptr ,这可能是可以避免的。但不幸的是,我正在编译的系统无法访问此版本的 C++。所以这个(相当基本的)方法现在就可以了。感谢所有做出贡献的人。

【问题讨论】:

  • HW.do_something();移动到if内?
  • 您不能有一个可以直接存储Wrapper1Wrapper2 的对象,因为Wrapper1Wrapper2 之间没有关系。如果它们具有共同的基本类型,则可以使用多态性。你仍然可以通过额外的步骤来实现这一点,比如使用std::variant
  • @appleapple 我更新了我的问题,最后进行了编辑,以解释为什么我想避免这种情况
  • 您是否考虑过只使用像using my_type = Wrapper1; 这样的类型别名?您是否需要根据运行时信息传播不同的类型?显示的示例没有,但这可能只是由于示例过于简单。
  • 看看奇怪的递归模板模式(crtp)

标签: c++ class inheritance


【解决方案1】:

你需要一个基类:

class HWVerBase                     { 
    private: 
        std::string _msg; 
    public:
        HWVerBase(std::string x): _msg(x) {} 
        virtual void do_something() = 0;
}; 

virtual void do_something() = 0; 表示 HWVer1 和 HWVer2 都必须实现此方法。

您现在可以创建一个std::shared_ptr&lt;HWVerBase&gt;,它可以容纳std::make_shared&lt;HWVer1&gt;std::make_shared&lt;HWVer2&gt;。由于这是一个指针,因此您需要-&gt;,如HW-&gt;do_something()

【讨论】:

  • 对不起,我对 shared_ptr 或 make_shared 不太熟悉。您介意再详细说明最后一点吗?
  • 也是我使用基本 C++ 的编译器,我认为无法访问 shared_ptr。
  • @cwm5412: shared_ptr 使用 C++ 已有十年了。它应该在您的教科书和编译器中。
  • 好吧,也许它有效,我会试试看。但要明确一点,这意味着 HWVer1 和 HWVer2 应该是基类 HWVerBase 的子类,是吗?
  • @cwm5412:是的:class HWVer1 : public HWVerBase { ...
【解决方案2】:

在这种情况下,某些东西需要比创建它的范围(大括号)更长的时间。这基本上就是动态分配的目的。

所以你似乎在寻找类似的东西:

class Base {
    // Stuff....
}

class Derived : public Base {
    // Other stuff....
}



Base* ptr = nullptr;   // or NULL if you don't have nullptr

if (HW_ver == 1) {
    ptr = new Base();
}
else if (HW_ver == 2) {
    ptr = new Derived();
}

ptr->do_something();

delete ptr;

当然,如果您能够使用 std::unique_ptr(甚至是过去的 C++ 标准中的 std::auto_ptr,这对于这种用法来说至少是可以的),那么您可以替换原始的 Base* ptr,这样您就不会必须记住确保调用了delete ptr

还要注意do_something 需要在Base 中声明为virtual。 (否则 ptr-&gt;do_something 将始终调用 Base 版本,即使涉及 Derived 对象。)

一本好的 C++ 书籍或这样的教育材料会很好地为您提供这些东西。这是非常基本的理解。互联网论坛上的问答不太可能涵盖您应该知道的所有内容。

【讨论】:

    【解决方案3】:

    您需要在对象还活着的时候调用该方法:

    int main(int argc, char ** argv){
        
        int HW_ver = 2;
        
        if (HW_ver == 1){
            Wrapper1 HW("Wrap1");
            HW.do_something();
        }
        
        else if (HW_ver == 2){
            Wrapper2 HW("Wrap2"); 
            HW.do_something();
        }
    }
    

    如果你需要调用多个方法,你可以让这两个继承自一个共同的基类,或者将所有的共同代码放在一个函数模板中。类似的东西:

     template <typename T> 
     void foo(T& t) {
         t.do_something();
         t.do_something_else();
         ... more ...
     }
    

    【讨论】:

      【解决方案4】:

      如果您不喜欢使用 new、delete 或继承,您可以尝试使用 std::variant 的解决方案: Wandbox Demo

      #include <iostream>
      #include <variant>
      
      // helper type for the visitor #4
      template <class... Ts> struct overloaded : Ts...
      {
        using Ts::operator()...;
      };
      // explicit deduction guide (not needed as of C++20)
      template <class... Ts> overloaded (Ts...) -> overloaded<Ts...>;
      
      class HWVer1
      {
      private:
        std::string _msg;
      
      public:
        HWVer1 (std::string x) : _msg (x) {}
        void
        do_something () const
        {
          std::cout << "hello from HWVer1: " << _msg << "\n";
        }
      };
      
      class HWVer2
      {
      private:
        std::string _msg;
      
      public:
        HWVer2 (std::string x) : _msg (x) {}
        void
        do_something () const
        {
          std::cout << "hello from HWVer2: " << _msg << "\n";
        }
      };
      
      typedef std::variant<std::monostate, HWVer1, HWVer2> Hardware;
      
      // clang-format off
      const auto handleMyHardware=overloaded{ 
                                [] (auto hw) {
                                  hw.do_something ();
                                },
                                [] (std::monostate) {
                                  std::cout << "not set" << std::endl;
                                } };
      // clang-format on
      int
      main ()
      {
        auto useHardware1 = true;
        auto hardware = Hardware{};
        if (useHardware1)
          {
            hardware = HWVer1{ "huhu" };
          }
        else
          {
            hardware = HWVer2{ "huhu" };
          }
        std::visit (handleMyHardware, hardware);
      }
      

      输出: 开始

      来自 HWVer1 的你好:huhu

      0

      完成

      我个人会尝试使用简单的结构来获得一些局部性。所以我可以看到 handleMyHardware lambda 表达式中发生了什么。 Wandbox Demo 2

      #include <iostream>
      #include <variant>
      
      // helper type for the visitor #4
      template <class... Ts> struct overloaded : Ts...
      {
        using Ts::operator()...;
      };
      // explicit deduction guide (not needed as of C++20)
      template <class... Ts> overloaded (Ts...) -> overloaded<Ts...>;
      
      struct HWVer1
      {
        std::string _msg;
      };
      
      struct HWVer2
      {
        std::string _msg;
      };
      
      typedef std::variant<std::monostate, HWVer1, HWVer2> Hardware;
      
      // clang-format off
      const auto handleMyHardware=overloaded{ 
                                [] (HWVer1 const& hw) {
                                  std::cout << "hello from HWVer1: " << hw._msg << "\n";
                                },
                                [] (HWVer2 const& hw) {
                                  std::cout << "hello from HWVer2: " << hw._msg << "\n";
                                },
                                [] (std::monostate) {
                                  std::cout << "hardware not set" << "\n";
                                } };
      // clang-format on
      int
      main ()
      {
        auto useHardware1 = true;
        auto hardware = Hardware{};
        if (useHardware1)
          {
            hardware = HWVer1{ "huhu" };
          }
        else
          {
            hardware = HWVer2{ "huhu" };
          }
        std::visit (handleMyHardware, hardware);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-28
        • 2010-12-14
        • 1970-01-01
        • 2015-12-29
        • 2011-11-26
        • 2018-01-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多