首页 文章

C派生类和多态调度

提问于
浏览
1

请考虑以下代码:

#include <iostream>
#include <string>

enum Type { T1, T2 };

class Base {
   public:
      std::string baseName;
      Type type;

      Base(const std::string& bn, Type t):
         baseName(bn), type(t) {}
};

class Derived1 : public Base
{
   public:
      std::string dName;
      int x = 10;

   Derived1(const std::string& bn, const std::string& dn):
      Base(bn, Type::T1), dName("Dervied1"+dn) {}

   int getX(void) const { return x; }
};

class Derived2 : public Base
{
   public:
      std::string dName;
      int y = 20;

   Derived2(const std::string& bn, const std::string& dn):
      Base(bn, Type::T2), dName("Derived2"+dn){}

   int getY(void) const { return y; }
};

void func(Base& b)
{
   if (b.type == Type::T1)
   {
      Derived1& d1 = static_cast<Derived1&>(b);
      std::cout << d1.baseName << " " << d1.dName << " " << d1.getX();
      std::cout << std::endl;
   }
   else
   {
      Derived2& d2 = static_cast<Derived2&>(b);
      std::cout << d2.baseName << " " << d2.dName << " " << d2.getY();
   }
};


int main(void)
{
   Derived1 d1("Base", "foo");
   func(d1);
   Derived2 d2("Base", "foo");
   func(d2);
}

要求是有一个函数可以接受基类值,然后根据派生实例的“类型”,做一些不同的事情 . 我的问题是 - 这是正确的做事方式,还是我错过了一些重要的设计模式 . 我记得读过使用static_cast或dynamic_cast意味着设计本身存在一些错误 . 我理解,理想情况下,基类可以具有派生类实现的虚函数,并且在运行时它们可以进行多态调度 . 但是,在这种情况下,每个派生类中有两个特定于这些类的函数,即 . getX和getY . 如何更改设计以使其更好,也许不使用演员?

谢谢!

2 回答

  • 1

    要求是有一个函数可以接受基类值,然后根据派生实例的“类型”,做一些不同的事情 .

    这正是多态性的全部意义所在 . 但是你没有按照它的使用方式使用它 .

    我的问题是 - 这是正确的做事方式

    没有 .

    我错过了一些重要的设计模式 .

    通过完全删除 Type 并在 Base 中引入虚拟方法可以更好地处理这个问题 .

    我理解,理想情况下,基类可以具有派生类实现的虚函数,并且在运行时它们可以进行多态调度 .

    究竟 .

    但是,在这种情况下,每个派生类中有两个特定于这些类的函数,即 . getX和getY .

    所以?正确使用多态不会阻止这种情况 .

    我如何更改设计以使其更好,也许不使用演员?

    正确使用多态性 . 例如:

    #include <iostream>
    #include <string>
    
    class Base
    {
    public:
        std::string baseName;
    
        Base(const std::string& bn):
          baseName(bn) {}
    
        virtual void doIt() = 0;
    };
    
    class Derived1 : public Base
    {
    public:
        std::string dName;
        int x = 10;
    
        Derived1(const std::string& bn, const std::string& dn):
          Base(bn), dName("Dervied1"+dn) {}
    
        int getX(void) const { return x; }
    
        void doIt() override
        {
            std::cout << baseName << " " << dName << " " << getX();
            std::cout << std::endl; 
        }
    };
    
    class Derived2 : public Base
    {
    public:
        std::string dName;
        int y = 20;
    
        Derived2(const std::string& bn, const std::string& dn):
          Base(bn), dName("Derived2"+dn) {}
    
        int getY(void) const { return y; }
    
        void doIt() override
        {
            std::cout << baseName << " " << dName << " " << getY(); 
        }
    };
    
    void func(Base& b)
    {
        b.doIt();
    }
    
    int main(void)
    {
        Derived1 d1("Base", "foo");
        func(d1);
        Derived2 d2("Base", "foo");
        func(d2);
    }
    

    然后通过移动公共代码使其更进一步,以便派生类可以共享它:

    #include <iostream>
    #include <string>
    
    class Base
    {
    public:
        std::string baseName;
    
        Base(const std::string& bn):
          baseName(bn) {}
    
        virtual void doIt()
        {
            std::cout << baseName;
        }
    };
    
    class Derived : public Base
    {
    public:
        std::string dName;
    
        Derived(const std::string& bn, const std::string& dn):
          Base(bn), dName(dn) {}
    
        void doIt() override
        {
            Base::doIt();
            std::cout << " " << dName;
        }
    };
    
    class Derived1 : public Derived
    {
    public:
        int x = 10;
    
        Derived1(const std::string& bn, const std::string& dn):
          Derived(bn, "Dervied1"+dn) {}
    
        int getX(void) const { return x; }
    
        void doIt() override
        {
            Derived::doIt();
            std::cout << " " << getX();
            std::cout << std::endl; 
        }
    };
    
    class Derived2 : public Derived
    {
    public:
        int y = 20;
    
        Derived2(const std::string& bn, const std::string& dn):
          Derived(bn, "Derived2"+dn) {}
    
        int getY(void) const { return y; }
    
        void doIt() override
        {
            Derived::doIt();
            std::cout << " " << getY(); 
        }
    };
    
    void func(Base& b)
    {
        b.doIt();
    }
    
    int main(void)
    {
        Derived1 d1("Base", "foo");
        func(d1);
        Derived2 d2("Base", "foo");
        func(d2);
    }
    
  • 2

    如果您可以选择使用 virtual 成员函数,如the other answer所述,则这是最佳使用方法 . 但是,有些情况下你没有那么奢侈 . 在这种情况下,您可以根据派生类型的类型构建调度机制 .

    #include <iostream>
    #include <string>
    #include <map>
    
    class Base {
       public:
          std::string baseName;
    
          Base(const std::string& bn): baseName(bn) {}
          virtual ~Base() {}
    
          // Don't store type ID per instance.
          // Make it a virtual function so derived classes
          // can return the same value for each instance.
          virtual int getTypeID() = 0;
    
          // Helper function for derived classes to use so each
          // derived class can have a unique type id associated
          // with it. This eliminates the need for having an enum.
          static int getNextTypeID();
          {
             static int typeID = 0;
             return ++typeID;
          }
    };
    
    
    class Derived1 : public Base
    {
       public:
          std::string dName;
          int x = 10;
    
          Derived1(const std::string& bn,
                   const std::string& dn):
                Base(bn), dName("Dervied1"+dn) {}
    
          // get type ID for this class.
          // Every instance of the class will return
          // same value.
          virtual int getTypeID()
          {
             return getTypeIDStatic();
          }
    
          // This is a crucial piece of function
          // that allows type based dispatch mechanism to work.
          static int getTypeIDStatic()
          {
             static int typeID = Base::getNextTypeID();
             return typeID;
          }
    
          int getX(void) const { return x; }
    };
    
    
    class Derived2 : public Base
    {
       public:
          std::string dName;
          int y = 20;
    
          Derived2(const std::string& bn,
                   const std::string& dn):
                Base(bn), dName("Derived2"+dn){}
    
          int getY(void) const { return y; }
    
          virtual int getTypeID()
          {
             return getTypeIDStatic();
          }
    
          static int getTypeIDStatic()
          {
             static int typeID = Base::getNextTypeID();
             return typeID;
          }
    };
    
    
    // Define a function type.
    using Function = void (*)(Base& b);
    
    // Keep a registry of functions that can be called for
    // different types derived from Base.
    std::map<int, Function>& getRegisteredFunctionsMap()
    {
       static std::map<int, Function> functionsMap;
       return functionsMap;
    }
    
    // Provide a mechanism to register functions for types
    // derived from Base.
    template <typename T>
    void registerFunction(Function f)
    {
       getRegisteredFunctionsMap()[T::getTypeIDStatic()] = f;
    }
    
    void func(Base& b)
    {
       // Check whether there is a function base on the type of b.
       std::map<int, Function>& functionsMap = getRegisteredFunctionsMap();
       std::map<int, Function>::iterator iter = functionsMap.find(b.getTypeID());
       if ( iter != functionsMap.end() )
       {
          // If yes, call it.
          iter->second(b);
       }
       else
       {
          // No function to deal with the type.
          // Deal with the situation.
       }
    };
    
    // A function that can be called when the real type is Derived1.
    void derived1Fun(Base& b)
    {
       // Assume that b is derived.
       Derived1& d1 = dynamic_cast<Derived1&>(b);
    
       // Now use d1.
       std::cout << d1.baseName << " " << d1.dName << " " << d1.getX();
       std::cout << std::endl;
    }
    
    // A function that can be called when the real type is Derived2.
    void derived2Fun(Base& b)
    {
       // Assume that b is Derived2.
       Derived2& d2 = dynamic_cast<Derived2&>(b);
    
       // Now use d2.
       std::cout << d2.baseName << " " << d2.dName << " " << d2.getY();
       std::cout << std::endl;
    }
    
    int main(void)
    {
       // Register functions for Derived1 and Derived2.
       registerFunction<Derived1>(derived1Fun);
       registerFunction<Derived2>(derived2Fun);
    
       // Make the function calls.
       Derived1 d1("Base", "foo");
       func(d1);
       Derived2 d2("Base", "foo");
       func(d2);
    }
    

    运行上述程序的输出:

    Base Dervied1foo 10
    Base Derived2foo 20
    

相关问题