C ++接口,继承,多态

时间:2017-04-15 19:42:18

标签: c++ inheritance interface polymorphism

想象一下这种情况:

  • 界面 I1 m1(),由 C1
  • 类实施
  • 界面 I2 m2(),由 C2
  • 类实施
  • 界面 I3 m3(),由 C3
  • 类实施

我想定义带有参数arg的函数。

void f1(I1 arg) {
    use m1()
}

void f2([I1, I2] arg) {
    use m1() and m2()
}

void f3([I2, I3] arg) {
    use m2() and m3()
}

然后我想定义:

  • “联盟”界面 I123 ,其中包含所有方法,由 C123
  • 实施
  • “联盟”类 C123 ,它继承现有类 C1 C2 C3 的实施方法。

然后我想实例化 C123 并将其与f1f2f3一起使用。

C123 obj;

f1(obj);
f2(obj);
f3(obj);

问题

  1. 这在C ++中是否可行?
  2. 没有接口可以吗?例如。函数f2将使用 [C1,C2] 而不是 [I1,I2]
  3. 代码

    #include <string>
    #include <iostream>
    
    using namespace std;
    
    class C1 {
    protected:
      int i;
    public:
      int getI() const { return i; }
      void setI(int i_) { i = i_; }
    };
    
    class C2 {
    protected:
      string s;
    public:
      string getS() const { return s; }
      void setS(string s_) { s = s_; }
    };
    
    class C3 {
    protected:
      float f;
    public:
      float getF() const { return f; }
      void setF(float f_) { f = f_; }
    };
    
    class C23 : public C2, public C3 {};
    class C123 : public C1, public C2, public C3 {};
    
    void f3(C23 arg) {
      arg.setS(to_string(arg.getF()));
    }
    
    
    int main() {
    
      C123 obj;
    
      f3(obj);
    
      std::cout << obj.getS();
    }
    

    错误消息

    a.cc: In function ‘int main()’:
    a.cc:42:9: error: could not convert ‘obj’ from ‘C123’ to ‘C23’
       f3(obj);
    

3 个答案:

答案 0 :(得分:0)

是的,这可以通过纯抽象类和多重继承来实现。由于I1I2I3之间没有冲突,因此多重继承不会导致任何问题。

示范性实施:

#include <iostream>

class I1 {
public:
    virtual void m1() = 0;
    virtual ~I1(){};
};

class C1 : public I1 {
public:
    void m1() override {
        std::cout << "m1() of a C1" << std::endl;
    }
    ~C1(){};
};

class I2 {
public:
    virtual void m2() = 0;
    virtual ~I2(){};
};

class C2 : public I2 {
public:
    void m2() override {
        std::cout << "m2() of a C2" << std::endl;
    }
    ~C2(){};
};

class I3 {
public:
    virtual void m3() = 0;
    virtual ~I3(){};
};

class C3 : public I3 {
public:
    void m3() override {
        std::cout << "m3() of a C3" << std::endl;
    }
    ~C3(){};
};

class I12 : public I1, public I2 {
public:
    virtual ~I12(){};
};

class I23 : public I2, public I3 {
public:
    virtual ~I23(){};
};

class I123 : public I12, public I23 {
public:
    virtual ~I123(){};
};

class C123 : public I123 {
public:
    void m1() override {
        std::cout << "m1() of a C123" << std::endl;
    }
    void m2() override {
        std::cout << "m2() of a C123" << std::endl;
    }
    void m3() override {
        std::cout << "m3() of a C123" << std::endl;
    }
    ~C123(){};
};

void f1(I1 &arg) {
    std::cout << "f1():" << std::endl;
    arg.m1();
}

void f2(I12 &arg) {
    std::cout << "f2():" << std::endl;
    arg.m1();
    arg.m2();
}

void f3(I23 &arg) {
    std::cout << "f3():" << std::endl;
    arg.m2();
    arg.m3();
}

int main() {
    C123 object;
    f1(object);
    f2(object);
    f3(object);
    return 0;
}

产生以下输出:

f1():
m1() of a C123
f2():
m1() of a C123
m2() of a C123
f3():
m2() of a C123
m3() of a C123

这表明C ++提供了满足您要求的多重继承。但是,我建议您阅读Effective C++ Item 40:明智地使用多重继承。本章建议仅在必要时使用虚拟基类。所以,当你确实拥有一个致命的MI钻石&#34;如果出现冲突,请将所有上述纯虚拟类用作虚拟基类(即通过: virtual public ....直接从中继承的所有类。成本较低,初始化规则较复杂。

更新

如果要在C123中继承C1,C2和C3的实现(请注意实现继承通常是错误的设计),请执行以下操作:

  • 使用I1I2I3I12I23I123作为虚拟基类,
  • 实施class C123 : virtual public I123, public C1, public C2, public C3而不重新实现m1()m2()m3()

结果输出

f1():
m1() of a C1
f2():
m1() of a C1
m2() of a C2
f3():
m2() of a C2
m3() of a C3

答案 1 :(得分:0)

C ++输入与Java的输入不同,因此通过重新评估您的需求来解决问题通常更容易。使用旧行为导出新对象有两种不同的方法,了解每种对象的好处非常重要。

继承是第一个,您基本上复制旧对象的功能,然后向其添加更多内容。当您有许多不同的父对象都实现相同的功能时,这就成了一个问题。第二个是组合,您可以在新对象中放置许多对象。然后,新对象可以“选择”使用哪些行为,通常会消除多重继承的歧义。它必须以您必须定义行为为代价,因为编译器无法对您的预期行为做出如此深刻的推理。

在您的情况下,我读到您有3个不同的对象,其行为不同,您需要撰写(而不是继承)他们的行为。您描述需要为组合对象中包含的任何m()调用I#;这很容易。

定义包装器对象,并有条件地调用每个接口提供的m函数:

class Wrapper {
    I1 slot1;
    I2 slot2;
    I3 slot3;

    Wrapper(i1 = I1.default(), i2 = I2.default(), i3 = I3.default()) {
        slot1 = i1;
        slot2 = i2;
        slot3 = i3;
    }

    void foo() {
        // The default instances don't do anything on m()
        slot1::m();
        slot2::m();
        slot3::m();
    }

}

毫无疑问,有很多改进和优化的空间,但这是一般的想法。

查看示例代码,您可能希望查看Logger的概念以进行调试。许多语言都使用它们,并且您“订阅”cout/cerr到程序的不同部分提供的不同记录器供稿。例如,您可能有一个物理记录器,另一个用于图形。

它们与您选择性输出对象中可用的不同数据类型非常相似。

答案 2 :(得分:0)

  
      
  1. 这在C ++中是否可行?
  2.   

是的,可以用C ++做到这一点。

Example solution using interfaces

#include <string>
#include <iostream>

using namespace std;

class I1 {
public:
    virtual int getI() const = 0;
    virtual void setI(int i) = 0;
};

class I2 {
public:
    virtual string getS() const = 0;
    virtual void setS(string s) = 0;
};

class I3 {
public:
    virtual float getF() const = 0;
    virtual void setF(float f) = 0;
};

class C1 : public I1 {
protected:
    int i;
public:
    int getI() const { return i; }
    void setI(int i_) { i = i_; }
};

class C12 : public I1, public I2 {
protected:
    int i;
    string s;
public:
    int getI() const { return i; }
    void setI(int i_) { i = i_; }
    string getS() const { return s; }
    void setS(string s_) { s = s_; }
};

class C123 : public I1, public I2, public I3 {
protected:
    int i;
    string s;
    float f;
public:
    int getI() const { return i; }
    void setI(int i_) { i = i_; }
    string getS() const { return s; }
    void setS(string s_) { s = s_; }
    float getF() const { return f; }
    void setF(float f_) { f = f_; }
};

template<class T>
void f1(const T& c1)
{
    cout << "f1:\n";
    cout << "  getI: " << c1.getI() << endl;
}

template<class T>
void f2(const T& c12)
{
    cout << "f2:\n";
    cout << "  getI: " << c12.getI() << endl;
    cout << "  getS: " << c12.getS() << endl;
}

template<class T>
void f3(const T& c23)
{
    cout << "f3:\n";
    cout << "  getS: " << c23.getS() << endl;
    cout << "  getF: " << c23.getF() << endl;
}

void test()
{
    C1 c1;
    c1.setI(1);
    f1(c1);

    cout << "\n===== " << endl;

    C12 c12;
    c12.setI(12);
    c12.setS("str12");
    f1(c12);
    f2(c12);

    cout << "\n===== " << endl;

    C123 c123;
    c123.setI(123);
    c123.setF(1.23f);
    c123.setS("str123");
    f1(c123);
    f2(c123);
    f3(c123);

    cout << "\n===== " << endl;
}

int main()
{
    test();
}
  
      
  1. 没有接口可以吗?例如。函数f2取[C1,C2]而不是[I1,I2]。
  2.   

是的,没有接口就可以做到这一点。

Solution using virtual inheritance (without using interfaces)

#include <string>
#include <iostream>

using namespace std;

class C1 {
protected:
    int i;
public:
    int getI() const { return i; }
    void setI(int i_) { i = i_; }
};

class C2 {
protected:
    string s;
public:
    string getS() const { return s; }
    void setS(string s_) { s = s_; }
};

class C3 {
protected:
    float f;
public:
    float getF() const { return f; }
    void setF(float f_) { f = f_; }
};

class C12 : public virtual C1, public virtual C2
{
};

class C23 : public virtual C2, public virtual C3
{
};

class C123 : public virtual C1, public virtual C12, public virtual C23
{
};


void f1(const C1& c1)
{
    cout << "f1:\n";
    cout << "  getI: " << c1.getI() << endl;
}

void f2(const C12& c12)
{
    cout << "f2:\n";
    cout << "  getI: " << c12.getI() << endl;
    cout << "  getS: " << c12.getS() << endl;
}

void f3(const C23& c23)
{
    cout << "f3:\n";
    cout << "  getS: " << c23.getS() << endl;
    cout << "  getF: " << c23.getF() << endl;
}

void test()
{
    C1 c1;
    c1.setI(1);
    f1(c1);

    cout << "\n===== " << endl;

    C12 c12;
    c12.setI(12);
    c12.setS("str12");
    f1(c12);
    f2(c12);

    cout << "\n===== " << endl;

    C123 c123;
    c123.setI(123);
    c123.setF(1.23f);
    c123.setS("str123");
    f1(c123);
    f2(c123);
    f3(c123);

    cout << "\n===== " << endl;
}

int main()
{
    test();
}

一些解释。如果你需要你的f2只采用C1和C2(没有C3)那么它必须是在C1和C2上都继承的单独类型。 f3也是如此。然后,如果您遵循该逻辑并创建类C12C23而不使用虚拟继承,那么现在应该从C12和C23继承的C123将以C2的多个副本结束,并且当您调用f2时和f3你会从getS获得不同的值。虚拟继承确保继承层次结构中只有一个类的副本。

  

a&#34; union&#34; C123类,它继承了现有类C1,C2,C3的实现方法

Solution that use virtual inheritance and interfaces

#include <string>
#include <iostream>

using namespace std;

class I1 {
public:
    virtual int getI() const = 0;
    virtual void setI(int i) = 0;
};

class I2 {
public:
    virtual string getS() const = 0;
    virtual void setS(string s) = 0;
};

class I3 {
public:
    virtual float getF() const = 0;
    virtual void setF(float f) = 0;
};

class I12 : virtual public I1, virtual public I2 {};
class I23 : virtual public I2, virtual public I3 {};
class I123 : virtual public I12, virtual public I23 {};

class C1 : virtual public I1 {
protected:
    int i;
public:
    int getI() const { return i; }
    void setI(int i_) { i = i_; }
};

class C2 : virtual public I2 {
protected:
    string s;
public:
    string getS() const { return s; }
    void setS(string s_) { s = s_; }
};

class C3 : virtual public I3 {
protected:
    float f;
public:
    float getF() const { return f; }
    void setF(float f_) { f = f_; }
};

class C12 : public I12, public C1, public C2 {};
class C123 : public I123, public C1, public C2, public C3 {};

void f1(const I1& c1)
{
    cout << "f1:\n";
    cout << "  getI: " << c1.getI() << endl;
}

void f2(const I12& c12)
{
    cout << "f2:\n";
    cout << "  getI: " << c12.getI() << endl;
    cout << "  getS: " << c12.getS() << endl;
}

void f3(const I123& c23)
{
    cout << "f3:\n";
    cout << "  getS: " << c23.getS() << endl;
    cout << "  getF: " << c23.getF() << endl;
}

void test()
{
    C1 c1;
    c1.setI(1);
    f1(c1);

    cout << "\n===== " << endl;

    C12 c12;
    c12.setI(12);
    c12.setS("str12");
    f1(c12);
    f2(c12);

    cout << "\n===== " << endl;

    C123 c123;
    c123.setI(123);
    c123.setF(1.23f);
    c123.setS("str123");
    f1(c123);
    f2(c123);
    f3(c123);

    cout << "\n===== " << endl;
}

int main()
{
    test();
}

所有解决方案都应产生此输出:

f1:
  getI: 1

=====
f1:
  getI: 12
f2:
  getI: 12
  getS: str12

=====
f1:
  getI: 123
f2:
  getI: 123
  getS: str123
f3:
  getS: str123
  getF: 1.23

=====