C ++派生类和多态调度

时间:2017-03-22 04:28:02

标签: c++ polymorphism

请考虑以下代码:

#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);
}

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

谢谢!

2 个答案:

答案 0 :(得分:2)

  

要求是拥有一个可以接受基类值的函数,然后根据派生实例的“类型”执行不同的操作。

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

  

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

没有

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

通过完全删除 public async task<ActionResult> SendMail(object obj) { var result = await SendEmail(ToEmailAddresses,body,emailSubject) return result } 并在Type中引入虚拟方法可以更好地处理这个问题。

  

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

完全。

  

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

所以?正确使用多态并不能阻止这种情况。

  

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

正确使用多态。例如:

Base

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

#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);
}

答案 1 :(得分:1)

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

#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