多个"级别"派生抽象类

时间:2017-11-05 16:30:21

标签: c++ templates inheritance abstract-class

我在C ++中有一个问题,我有

  • 一个具有公共成员和纯虚函数的抽象基类。这是A在下面的代码中。
  • 具有不同成员和功能的多个派生类。以下代码中为BC
  • 实现虚函数且没有新成员的多个派生类。以下代码中为do_stuff_1do_stuff_2

到目前为止,我的解决方案是混合模板和抽象类。目标是保持指向抽象类BC的指针并调用do_stuff。我的问题是

  1. 我的解决方案是好的吗?
  2. 如果没有,是否有更好的解决方案?
  3. 如果是这样,我应该注意一些陷阱吗?
  4. 到目前为止,这是我的解决方案

    #include <iostream>
    #include <memory>
    
    /* base virtual class */
    class A {
    public:
      int a;
    
      A(int a) : a(a) {}
    
      virtual void do_stuff() = 0;
    };
    
    /* concrete implementations of do_stuf */
    template<class T>
    class do_stuff_1 : public T {
    public:
      using T::T;
    
      void do_stuff(){
        std::cout << "do_stuff_1 " << this->a << std::endl;
      }
    };
    
    template<class T>
    class do_stuff_2 : public T {
    public:
      using T::T;
    
      void do_stuff(){
        std::cout << "do_stuff_2 " << this->a + 1 << std::endl;
      }
    };
    
    /* derived classes from A */
    class B : public A {
    public:
      int b;         // one member here but many more in my application
    
      B(int a, int b): A(a), b(b) {}
    };
    
    class C : public A {
    public:
      std::string c; // one member here but many more in my application
    
      C(int a, std::string c): A(a), c(c) {}
    };
    
    int main() {
      std::unique_ptr<B> x;
      x.reset(new do_stuff_1<B>(1, 1));
      x->do_stuff();
      std::cout << x->b << std::endl;
      x.reset(new do_stuff_2<B>(1, 2));
      x->do_stuff();
      std::cout << x->b << std::endl;
    
      std::unique_ptr<C> z;
      z.reset(new do_stuff_1<C>(1, "Yo"));
      z->do_stuff();
      std::cout << z->c << std::endl;
      z.reset(new do_stuff_2<C>(1, "Hello"));
      z->do_stuff();
      std::cout << z->c << std::endl;
    
      return 0;
    }
    

    结果是

    do_stuff_1 1                                                                                                                                     
    1                                                                                                                                                
    do_stuff_2 2                                                                                                                                     
    2                                                                                                                                                
    do_stuff_1 1                                                                                                                                     
    Yo                                                                                                                                               
    do_stuff_2 2                                                                                                                                     
    Hello 
    

2 个答案:

答案 0 :(得分:1)

你的解决方案似乎很好。它是一种编译时方法,您可以创建4个不同的对象。

主要缺点是:

  • 除非您实例化模板,否则您不会知道do_stuff()代码是否正确。
  • 您可以使用非基类A的类来实例化do_stuff_1do_stuff_2。您应该至少在模板中使用override以确保它覆盖虚函数。< / LI>

这是解决这些问题的一个小改进:

template<class T>
class do_stuff_2 : public T {
public:
  using T::T;

  void do_stuff() override {
    static_assert (std::is_base_of<A, T>::value, "T should be derived from A");
    std::cout << "do_stuff_2 " << this->a + 1 << std::endl;
  }
};

顺便说一句,使用make_unique会很好。

答案 1 :(得分:0)

对我来说看起来像某种政策,这可能看起来像:

#include <iostream>
#include <memory>
#include <type_traits>

struct AwayToDoTheStuff {
    virtual void operator()(int a) = 0;

    virtual ~AwayToDoTheStuff() {}
};

/* concrete implementations of do_stuf */
class HowToDoStuff1 : public AwayToDoTheStuff {
    public:
        void operator()(int a) override {
            std::cout << "do_stuff_1 " << a << std::endl;
        }
};

class HowToDoStuff2 : public AwayToDoTheStuff {
    public:
        void operator()(int a) override {
            std::cout << "do_stuff_2 " << a + 1 << std::endl;
        }
};

/* base virtual class */
template <class HowToDoStuff>
class A {
    public:
        int a;

        A(int a) : a(a) {}

        void do_stuff() {
            static_assert(std::is_base_of<AwayToDoTheStuff, HowToDoStuff>::value);
            HowToDoStuff()(a);
        }
};

/* derived classes from A */
template <class HowToDoStuff>
class B : public A<HowToDoStuff> {
    public:
        int b;         // one member here but many more in my application

        B(int a, int b): A<HowToDoStuff>(a), b(b) {}
};

template <class HowToDoStuff>
class C : public A<HowToDoStuff> {
    public:
        std::string c; // one member here but many more in my application

        C(int a, std::string c): A<HowToDoStuff>(a), c(c) {}
};

int main() {
    B<HowToDoStuff1>(1, 1).do_stuff();
    B<HowToDoStuff2>(1, 2).do_stuff();

    C<HowToDoStuff1>(1, "Yo").do_stuff();
    C<HowToDoStuff2>(1, "Hello").do_stuff();

    return 0;
}

但我必须说这很难说一个解决方案是否与一个非常通用的例子匹配得很好。我希望它能在某些方面帮助你......

修改

您似乎需要拥有一个公共基类,以便您可以将对象B和C传递给void f(A &a);

类的公共函数

然后我的例子可以这样调整:

/* base virtual class */
class A {
    public:
        void do_stuff() = 0;
};

template <class HowToDoStuff>
class Policy_A : public A {
    public:
        int a;

        A(int a) : a(a) {}

        void do_stuff() override {
            static_assert(std::is_base_of<AwayToDoTheStuff, HowToDoStuff>::value);
            HowToDoStuff()(a);
        }
};

/* derived classes from A */
template <class HowToDoStuff>
class B : public Policy_A<HowToDoStuff> {
    public:
        int b;         // one member here but many more in my application

        B(int a, int b): Policy_A<HowToDoStuff>(a), b(b) {}
};

template <class HowToDoStuff>
class C : public Policy_A<HowToDoStuff> {
    public:
        std::string c; // one member here but many more in my application

        C(int a, std::string c): Policy_A<HowToDoStuff>(a), c(c) {}
};

这样就可以在不透明的A对象上调用do_stuff。

您也可以在创建时传递HowToDoStuff对象:

/* base virtual class */
class A {
    std::unique_ptr<AwayToDoTheStuff> _stuffer;
    public:
        int a;

        A(std::unique_ptr<AwayToDoTheStuff> stuffer, int a) : _stuffer(std::move(stuffer)), a(a) {}

        void do_stuff() {
            (*_stuffer)(a);
        }
};

/* derived classes from A */
class B : public A {
    public:
        int b;         // one member here but many more in my application

        B(std::unique_ptr<AwayToDoTheStuff> &stuffer, int a, int b): A(std::move(stuffer), a), b(b) {}
};

class C : public A {
    public:
        std::string c; // one member here but many more in my application

        C(std::unique_ptr<AwayToDoTheStuff> &stuffer, int a, std::string c): A(std::move(stuffer), a), c(c) {}
};

int main() {

    auto stuffer1forB = std::unique_ptr<AwayToDoTheStuff>(new HowToDoStuff1);
    auto stuffer2forB = std::unique_ptr<AwayToDoTheStuff>(new HowToDoStuff2);

    B(stuffer1forB, 1, 1).do_stuff();
    B(stuffer2forB, 1, 2).do_stuff();

    auto stuffer1forC = std::unique_ptr<AwayToDoTheStuff>(new HowToDoStuff1);
    auto stuffer2forC = std::unique_ptr<AwayToDoTheStuff>(new HowToDoStuff2);

    C(stuffer1forC, 1, "Yo").do_stuff();
    C(stuffer2forC, 1, "Hello").do_stuff();

    return 0;
}