【问题标题】:C++ interface inheritance different arguments methodC++接口继承不同参数方法
【发布时间】:2018-09-10 20:05:42
【问题描述】:
class Base
{
    public:
    virtual void print() = 0;
};

class A : public Base
{
    int mClassA;
    public:
    A() : mClassA(1) {}
    void print() override { std::cout << "print A" << std::endl; }
    void foo( A& arg ) { std::cout << mClassA << std::endl; }
};

class B : public Base
{
    int mClassB;
    public:
    B() : mClassB(2) {}
    void print() override { std::cout << "print B" << std::endl; }
    void foo( B& arg ) { std::cout << mClassB << std::endl; }
};

所以我得到了类似的类结构。我应该采取什么方法来调用没有 dynamic_cast 的 foo ?

int main()
{
  Base * obj1 = new A();
  Base * obj2 = new A();
  dynamic_cast<A*>(obj1)->foo(*dynamic_cast<A*>(obj2));
}

我可以使用基类参数创建 foo 方法,但我想确保我将 A 或 B 对象作为参数传递。

【问题讨论】:

  • 可以用虚函数virtual void foo()吗?
  • I could create foo method with base class argument but I want to be sure that I'm passing A or B obejct as an argument. 你甚至不能创建Base 的实例,那么这句话到底是什么意思?

标签: c++ inheritance polymorphism


【解决方案1】:

您可以使用模板来确保类的成员函数之一的特定参数至少具有特定类型。请参阅以下代码来说明这一点:

template <class P>
class Base
{
public:
    Base(int nr) : mClass(nr) {}
    virtual void print() = 0;
    virtual void foo( P& arg ) { std::cout << mClass << std::endl; }
protected:
    int mClass;

};

class A : public Base<A>
{
public:
    A() : Base(1) {}
    void print() override { std::cout << "print A" << std::endl; }
    virtual void foo( A& arg ) override { Base::foo(arg); cout << "is A for sure" << endl; }
};

class B : public Base<B>
{
public:
    B() : Base(2) {}
    void print() override { std::cout << "print A" << std::endl; }
    virtual void foo( B& arg ) override { Base::foo(arg); cout << "is B for sure" << endl; }
};

int main()
{
    Base<A> * obj1 = new A();
    A* obj2 = new A();
    obj1->foo(*obj2);

    Base<B> * objb1 = new B();
    B* objb2 = new B();
    objb1->foo(*objb2);

//  objb1->foo(*obj2);
//  Non-const lvalue reference to type 'B' cannot bind to a value of unrelated type 'A'
}

【讨论】:

    【解决方案2】:

    听起来你想做这样的事情:

    class Base
    {
        public:
        virtual void foo(Base&) = 0;
    };
    
    class A : public Base
    {
        public:
        void foo(A&);
    };
    
    class B : public Base
    {
        public:
        void foo(B&);
    };
    

    在面向对象的设计中,这称为covariance(特别是“协变方法参数类型”)。

    问题在于这违背了良好的面向对象设计的原则。 Liskov substitution principle 表示,如果您有一个基类 Base,那么 Base 的子类的任何实例都需要可互换 - 但您希望 Base 的某些子类不能与 Base 的其他子类一起使用. (这过于简单化了,但网上有很多讨论更详细。)

    如果您想这样做 - 如果它是您情况下的最佳解决方案,尽管有 Liskov 替换原则的一般建议 - 那么您可以自己实施检查。

    void A::foo(Base& base_arg) {
        // This will throw a runtime exception if the wrong type
        A& arg = dynamic_cast<A&>(base_arg);
    
        std::cout << mClassA << std::endl;
    }
    

    请注意,您现在正在牺牲一些编译时类型安全性 - 如果您不小心尝试使用 B 的实例调用 A::foo,则在代码运行并出现异常之前您不会知道。 (这就是虚函数/动态调度/多态的全部意义——行为是在运行时确定的。)

    另一种方法是使用模板,例如@Stephen Lechner's solution。这放弃了运行时多态性,但它保持了强大的类型安全性,并且更好地遵循了传统的 OO 设计。

    Wikipedia article on covariance 有更多讨论,包括更多示例代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-13
      • 2011-01-17
      • 2011-03-24
      • 1970-01-01
      • 2023-03-14
      • 2015-10-17
      • 2023-03-26
      相关资源
      最近更新 更多