Интерфейс C ++ наследование разных аргументов - PullRequest
0 голосов
/ 10 сентября 2018
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; }
};

Итак, я получил структуру классов, похожую на эту.Какой подход я должен использовать для вызова foo без dynamic_cast каждый раз?

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

Я мог бы создать метод foo с аргументом базового класса, но я хочу быть уверен, что я передаю объект A или B в качестве аргумента.

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Звучит так, будто вы хотите сделать что-то вроде этого:

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

class A : public Base
{
    public:
    void foo(A&);
};

class B : public Base
{
    public:
    void foo(B&);
};

В объектно-ориентированном проектировании это известно как ковариация (в частности, тип аргумента ковариантного метода)").

Проблема в том, что это противоречит принципам хорошего объектно-ориентированного проектирования.Принцип подстановки Лискова гласит, что если у вас есть базовый класс Base, то любые экземпляры подклассов Base должны быть взаимозаменяемыми - но вы хотите, чтобы некоторые подклассы Base не работали сдругие подклассы Base.(Это упрощение, но есть много дискуссий в Интернете с более подробной информацией.)

Если вы хотите сделать это - если это лучшее решение в вашем случае, несмотря на общие рекомендации замены Лисковапринцип - тогда вы можете выполнить проверки самостоятельно.

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;
}

Обратите внимание, что вы жертвуете некоторой безопасностью типа времени компиляции сейчас - если вы случайно попытаетесь вызвать A::foo с экземпляром B,вы не будете знать, пока код не будет запущен, и вы получите исключение.(В этом весь смысл виртуальных функций / динамической диспетчеризации / полиморфизма - поведение определяется во время выполнения.)

Другой подход заключается в использовании шаблонов, таких как @ решение Стивена Лехнера .Это избавляет от полиморфизма во время выполнения, но сохраняет строгую безопасность типов и лучше следует традиционному OO-дизайну.

Статья Википедии о ковариации содержит гораздо больше обсуждений, включая дополнительный пример кода.

0 голосов
/ 10 сентября 2018

Вы можете использовать шаблоны, чтобы убедиться, что определенный параметр одной из функций-членов класса имеет по крайней мере определенный тип. Смотрите следующий код, иллюстрирующий это:

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'
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...