实现后期多态性的最佳方法

时间:2018-05-15 23:55:55

标签: c++ c++11 inheritance polymorphism virtual-functions

我有几个不同的模板纯抽象类。我从这些派生出来获得一堆类,从那里,我可以使用它们来制作一堆对象。我想将所有这些对象放入容器中。但是,它们都是不同的类型。我想知道如何完成这个后期多态性。

说这是我现有的预先存在的代码:

public static class AssemblyHelper
{
    //--------------------------------------------------------------------------------
    /// <summary>
    /// Redirection hack because Azure functions don't support it.
    /// How to use:  
    ///     If you get an error that a certain version of a dll can't be found:
    ///         1) deploy that particular dll in any project subfolder 
    ///         2) In your azure function static constructor, Call 
    ///             AssemblyHelper.IncludeSupplementalDllsWhenBinding()
    ///         
    /// This will hook the binding calls and look for a matching dll anywhere 
    /// in the $HOME folder tree.  
    /// </summary>
    //--------------------------------------------------------------------------------
    public static void IncludeSupplementalDllsWhenBinding()
    {
        var searching = false;

        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            // This prevents a stack overflow
            if(searching) return null;
            var requestedAssembly = new AssemblyName(args.Name);
            searching = true;

            Assembly foundAssembly = null;
            try
            {
                foundAssembly = Assembly.Load(requestedAssembly);
            }
            catch(Exception e)
            {
                Debug.WriteLine($"Could not load assembly: {args.Name} because {e.Message}");
            }

            searching  = false;

            if(foundAssembly == null)
            {
                var home = Environment.GetEnvironmentVariable("HOME") ?? ".";

                var possibleFiles = Directory.GetFiles(home, requestedAssembly.Name + ".dll", SearchOption.AllDirectories);
                foreach (var file in possibleFiles)
                {
                    var possibleAssembly = AssemblyName.GetAssemblyName(file);
                    if (possibleAssembly.Version == requestedAssembly.Version)
                    {
                        foundAssembly = Assembly.Load(possibleAssembly);
                        break;
                    }
                }
            }

            return foundAssembly;
        };
    }
}

有了更多的课程,#include <iostream> template<typename T> class A{ public: A() : m_num(1.0) {}; virtual ~A() {}; virtual void printNum() const = 0; protected: T m_num; }; template<typename T> class B{ public: B() : m_num(2.0) {}; virtual ~B() {}; virtual void printTwiceNum() const = 0; protected: T m_num; }; class A_example : public A<int> { public: A_example() : A<int>() {}; void printNum() const { std::cout << m_num << "\n"; }; }; class B_example : public B<int> { public: B_example() : B<int>() {}; void printTwiceNum() const { std::cout << 2*m_num << "\n"; }; }; int main(){ A_example first; B_example second; first.printNum(); second.printTwiceNum(); return 0; } 内部可能会变得非常混乱。理想情况下,我可以jut遍历容器并在每个元素上调用main()。我的第一个想法是使用print()。这似乎有效:

std::vector<unique_ptr<Base>>

这很酷,因为我不必更改#include <iostream> #include <vector> // new include #include <memory> // new include #include <utility> // new include // new Base class here class Base{ public: virtual ~Base(){}; }; template<typename T> class A : public Base{ // new inheritance here public: A() : m_num(1.0) {}; virtual ~A() {}; virtual void printNum() const = 0; protected: T m_num; }; template<typename T> class B : public Base{ // new inheritance here as well public: B() : m_num(2.0) {}; virtual ~B() {}; virtual void printTwiceNum() const = 0; protected: T m_num; }; class A_example : public A<int> { public: A_example() : A<int>() {}; void printNum() const { std::cout << m_num << "\n"; }; }; class B_example : public B<int> { public: B_example() : B<int>() {}; void printTwiceNum() const { std::cout << 2*m_num << "\n"; }; }; int main(){ std::vector<std::unique_ptr<Base>> v; v.emplace_back( new A_example() ); v.emplace_back( new B_example() ); //v[0]->printNum(); // nope //v[1]->printTwiceNum(); // nope return 0; } A_example,而我在B_exampleA中更改的内容是我添加的{{1} }。但是,我不知道如何调用每个元素B函数。有没有办法调用: public Baseprint***函数,并自动识别它们?

1 个答案:

答案 0 :(得分:4)

最简单的方法是创建一个虚函数Base::print并让派生类实现它。但这并不总是合适的。

另一种方法是分支dynamic_cast次转化。前提是某些功能仅适用于某些类。但是,这可能会变得毛茸茸,特别是在使用类模板时,因为您必须处理所有预期的模板参数。

要概括这一点,您可以使用接口类。我们假设您有很多不同的类,但只有少量的打印版本。在这种情况下,这样做可能有意义:

class PrintNumInterface {
public:
    virtual void printNum() const = 0;
};

class PrintTwiceNumInterface {
public:
    virtual void printTwiceNum() const = 0;
};

template<typename T> class A : public Base, public PrintNumInterface { ... };
template<typename T> class B : public Base, public PrintTwiceNumInterface { ... };

现在,无论您需要处理多少额外的类或模板扩展,您只需要处理这些接口:

for (auto& p : v)
{
    if (PrintNumInterface* iface = dynamic_cast<PrintNumInterface*>(p.get())
        iface->printNum();
    else if (PrintTwiceNumInterface* iface = dynamic_cast<PrintTwiceNumInterface*>(p.get())
        iface->printTwiceNum();
}