通过构造函数动态确定类的实现

时间:2018-10-26 11:12:15

标签: c++ class inheritance dynamic c++17

我想创建一个表现某种方式的类-例如根据传递给其构造函数的“类型”,从函数double getValue(const int& x) const中吐出某些值。现在我有两种方法:

  1. 存储传入的“类型”,然后在每次调用switch时评估getValue语句,以决定使用哪种实现。
  2. 在传入的“类型”(在构造函数中)上使用switch语句创建一个表示所需实现的内部对象。因此,switch本身不再需要getValue

方法1“似乎”效率低下,因为每次我调用switch时都会调用getValue。方法2似乎有些笨拙,因为我需要利用<memory>,这也使复制/分配我的类变得很简单。

还有其他更清洁的方法来解决这样的问题吗?

代码示例:

#include <memory>

enum class ImplType { Simple1, Simple2 /* more cases */ };

class MyClass1
{
private:
    const ImplType implType;

public:
    MyClass1(const ImplType& implType) : implType(implType) { }

    double getValue(const int& x) const
    {
        switch (implType)
        {
        case ImplType::Simple1: return 1; /* some implemention */
        case ImplType::Simple2: return 2; /* some implemention */
        }
    }
};

class MyClass2
{
private:
    struct Impl { virtual double getValue(const int& x) const = 0; };
    struct ImplSimple1 : Impl { double getValue(const int& x) const override { return 1; /* some implemention */ } };
    struct ImplSimple2 : Impl { double getValue(const int& x) const override { return 2; /* some implemention */ } };

    const std::unique_ptr<Impl> impl;

public:
    MyClass2(const ImplType& implType) : impl(std::move(createImplPtr(implType))) { }

    static std::unique_ptr<Impl> createImplPtr(const ImplType& implType)
    { 
        switch (implType)
        {
        case ImplType::Simple1: return std::make_unique<ImplSimple1>();
        case ImplType::Simple2: return std::make_unique<ImplSimple2>();
        }
    }

    double getValue(const int& x) const { return impl->getValue(x); }
};


int main()
{
    MyClass1 my1(ImplType::Simple1);
    MyClass2 my2(ImplType::Simple1);

    return 0;
}

3 个答案:

答案 0 :(得分:1)

您的代码基本上是在模仿虚拟方法(粗俗地说:相同的接口,但是在运行时选择了实现),因此,如果您确实使用虚拟方法,则代码可以更加简洁:

 #include <memory>

 struct base {
     virtual double getValue(const int& x) const = 0;
 };

 struct impl1 : base {
     double getValue(const int& x) { return 1.0; }
 };

 struct impl2 : base {
     double getValue(const int& x) { return 2.0; }
 };
 // ... maybe more...

 enum select { impl1s, impl2s };
 base* make_impl( select s) {
     if (s == impl1s) return new impl1();
     if (s == impl2s) return new impl2();
 }

 int main() {
     std::shared_ptr<base> x{ make_impl(impl1) };
 }

不确定这是否是您想要的。顺便说一句,使用<memory>不应让您感到“笨拙”,而应该为我们在c ++中拥有如此出色的工具而感到自豪;)。

编辑:如果您不希望用户使用(智能)指针,则将以上内容包装在另一个类中:

struct foo {
    shared_ptr<base> impl;
    foo( select s) : impl( make_impl(s) ) {}
    double getValue(const int& x) { return impl.getValue(x); }
};

现在用户可以做

int main() {
    auto f1 { impl1s };
    auto f2 { impl2s };
    f1.getValue(1);
    f2.getValue(2);
}        

答案 1 :(得分:1)

如果您可以选择一组封闭的类型,则需要std::variant

using MyClass = std::variant<MyClass1, MyClass2, MyClass3, /* ... */>;

它不使用动态分配-基本上是union的类型安全的现代替代方案。

答案 2 :(得分:0)

更多面向对象的方法:

class Interface
{
    public:
        virtual int getValue() = 0;
};

class GetValueImplementation1 : public Interface
{
    public:
        int getValue() {return 1;}
};

class GetValueImplementation2 : public Interface
{
    public:
        int getValue() {return 2;}
};

class GeneralClass
{
    public:
        GeneralClass(Interface *interface) : interface(interface) {}
        ~GeneralClass() 
        {
            if (interface)
                delete interface;
        }

        int getValue() { return interface->getValue(); }

    private:
        Interface *interface;
};

因此,在这种情况下,您可以使用它而无需任何指针:

int main()
{
    GeneralClass obj1(new GetValueImplementation1());
    GeneralClass obj2(new GetValueImplementation2());

    cout << obj1.getValue() << " " << obj2.getValue();
    return 0;
}

输出将是:

1 2

但是在这种情况下,您应该小心使用空指针或在GeneralClass内使用智能指针。