【问题标题】:Need recommendations on multiple inheritance and optional function overriding C++需要关于多重继承和覆盖 C++ 的可选函数的建议
【发布时间】:2017-09-20 14:30:11
【问题描述】:

我有一个问题,我想解决一个问题,我希望有一些抽象基类,它继承自多个类,派生可以选择性地覆盖它们的方法,而无需声明额外的类。我将在这里举例说明我想要实现的目标:

#include <iostream>

using namespace std;

class A
{
    public:
        virtual void foo() = 0;

    protected:
        A() {}
};

class A1 : public A
{
    public:
        A1() : A() {}

        void foo() { cout << "A1 foo" << endl; };
};

class A2 : public A
{
    public:
        A2() : A() {}

        void foo() { cout << "A2 foo" << endl; };
};

class B
{
    public:
        virtual void bar() { cout << "B bar: " << endl; }
};

class B1 : public B
{
    public:
        void bar()
        {
            cout << "B1 bar wrapper begin" << endl;
            B::bar();
            cout << "B1 bar wrapper end" << endl;
        }
};

/*
  ???
  pure virtual class C
  enforce derived classes to inherit something of type A
  enforce derived classes to inherit something of type B

  class C1 : public A1, either B or B1 ??? templates???
  {

  }

  class C2 : public A2, either B or B1 ??? templates???
  {

  }

  Can this be done without having to define classes CA1B, CA2B, CA1B1, CA2B1, etc.?
*/

int main(int argc, char *argv[])
{
    A1 a1;
    a1.foo();
    A2 a2;
    a2.foo();

/*
    C1 c1b with type B
    C1 c1b1 with type B1
    C2 c2b with type B
    C2 c2b1 with type B1

    put c1b, c1b1, c2b, c2b1 in a list named "combinations"

    cout << "Printing combinations" << endl;
    for (auto i : combinations)
    {
        i->foo();
        i->bar();
    }
*/

    return 0;
}

理论上输出是:

A1 foo
A2 foo
Printing combinations
A1 foo
B bar
A1 foo
B1 bar wrapper begin
B bar
B1 bar wrapper end
A2 foo
B bar
A2 foo
B1 bar wrapper begin
B bar
B1 bar wrapper end

如果有办法通过某种设计模式来实现这一点,或者我使用了不好的方法,请告诉我。我正在使用 C++11。

【问题讨论】:

    标签: c++ templates inheritance polymorphism overriding


    【解决方案1】:

    您的用例大喊“带有约束的模板”。您缺少的是如何检查和编码模板参数是否从正确的类继承。你可以用std::is_base_of来做到这一点

    template<class A_, class B_,
             typename std::enable_if<std::is_base_of<A, A_>::value>, int>::type = 0,
             typename std::enable_if<std::is_base_of<B, B_>::value>, int>::type = 0>
    class C : public A_, public B_
    {
    
    };
    

    它是这样工作的:

    std::enable_if 将有一个type(在我们的例子中是一个int),如果它输入的布尔表达式为真。否则那里没有类型,模板将无法编译。如果那里有类型,那么我们得到一个非类型模板参数,我们给它默认值0。分配默认值使我们能够使用两个参数来实例化模板。

    您可以在 &lt;type_traits&gt; 标头中找到这些实用程序以及更多内容。

    【讨论】:

    • 这样的东西可以多态使用吗?例如,如果我创建一个shared_ptr&lt;C&lt;A1, B&gt;&gt; 对象和另一个shared_ptr&lt;C&lt;A1, B1&gt;&gt; 对象,这两个对象可以存储在list&lt;shared_ptr&lt;C&lt;A, B&gt;&gt;&gt; 中吗?我不确定 C 类需要什么才能将某些派生版本转换为 C&lt;A, B&gt;。
    • @George - 不。无论是使用模板还是明确说明(如您的问题)C&lt;A, B&gt; 和 C&lt;A1, B&gt; 都不是多态关系。
    • @George - 但您可以将所有这些类放入B* 的容器或A* 的容器中。
    【解决方案2】:

    不久前我对这个主题很感兴趣,所以我有一些策略。我认为套接字程序是这种抽象类型的完美演示。最简单的事情之一是声明一个基本头文件,其中包含您希望在您声明的类的任何对象中可用的函数和变量。然后在将 ITSELF 设置为模板参数时创建从基类继承的类。例如:

    template<class P> class Socket {
      protected:
       int sock;
       char datagram[4096];
       struct sockaddr_in server;
       struct sockaddr_in b;
      public:
       void dport(int port){server.sin_port=htons(port);}
       int CONNECT(){return connect(sock , (struct sockaddr *)&server , sizeof(server));};
      template <class Dst> inline void dst(Dst d){server.sin_addr.s_addr=inet_addr(d);}
      template <class Src> void src(Src s){b.sin_addr.s_addr=inet_addr(s);}  
      int LISTEN(int port){b.sin_port=htons(port); return bind(sock,
      (sockaddr*)&b, sizeof(b));}
    

    在这一点上,您可以通过多种方式进行专攻。我最喜欢的是创建通过将自己的名称作为模板参数传递给 base 来继承的新类。

    class UDP : public Socket<UDP> {
    public:
        UDP(){server.sin_family=AF_INET;
        this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);}
        template <class Msg> int SEND(Msg m, int length){
        return sendto(this->sock, m, length, MSG_DONTWAIT, (sockaddr*)&this-
        >server, sizeof(this->server));}
        void RECV(void* buf, size_t size){socklen_t fromlen = sizeof(sockaddr);
        ssize_t i = recvfrom(this->sock, buf, size, MSG_WAITALL,
        (sockaddr*)&this->b, &fromlen);}
    };
    
    class TCP : public Socket<TCP> {
    public:
     TCP(){this->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); this-
     >server.sin_family=AF_INET;}
     int SEND(std::string buf){return send(this->sock, buf.c_str(), buf.size(), 0);}   //TODO: Error Handling
     void RECV(){char cur; while (read(this->sock, &cur, 1) > 0 ) {cout << cur;}}};
    

    现在 UDP 套接字使用 recvfrom() 和 sendto(),而 TCP 套接字使用 send() 和 read()。所有这些调用现在都有效:

    Socket<TCP> t1;
    Socket<UDP> u1;
    TCP t2;
    UDP u2;
    

    如果不重复每个基类的代码,我想不出任何方法来编写基类的特化。创建派生类更容易。

    【讨论】:

      猜你喜欢
      • 2011-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多