C ++ переопределить виртуальный метод с абстрактным классом в качестве параметра - PullRequest
0 голосов
/ 19 сентября 2018

У меня есть следующий абстрактный класс

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

и несколько классов, унаследованных от этого класса.например,

class B : public A {
public:
    void foo(A* a); // implementation in a separete file
}

Однако я хочу, чтобы класс B принимал себя в качестве аргумента в foo

void foo(B* b);

Возможно ли это сделать в C ++?Я рассмотрел шаблон, но синтаксис допускает слишком большую гибкость.Можно написать class B: public A<B>, но я хочу ошибку компилятора с class B: public A<C>.

- Правка -

Кажется, что я использовал абстрактныекласс не оправдан.Позвольте мне прояснить мою ситуацию.

Я использую полиморфное поведение A в отдельной функции.В дополнение к этому, я хочу определить функцию, которая принимает аргумент того же типа, что и приведенный выше.Я пытаюсь написать функцию, которая определяет расстояние между двумя объектами производного класса.Расстояние определяется только между объектами одного класса (b1 и b2, или c1 и c2, но не b1 и c2).Я также хотел бы получить доступ к этой функции расстояния в общем виде, насколько это возможно.

- Редактировать 2 -

Кассио показал, почему невозможно выполнить проверку на основе компилятора.Решение zar добавляет немного больше структуры в код с проверкой ошибок во время выполнения.

Ответы [ 2 ]

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

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

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

class B : public A {
public:
    void foo(A* a)
    {
        if (dynamic_cast<B*> (a) == NULL)
            std::cout << "wrong type, expecting type B\r\n";
    }
};

class C : public A {
public:
    void foo(A* a)
    {
        if (dynamic_cast<C*> (a) == NULL)
            std::cout << "wrong type, expecting type C\r\n";
    }
};

int main()
{
    B * b1 = new B;
    B * b2 = new B;

    C * c1 = new C;
    C * c2 = new C;

    b2->foo(c1); // bad

    c1->foo(b1); // bad

    b2->foo(b1); // good

    delete b1;
    delete b2;
    delete c1;
    delete c2;
}

см. Также dynamic_cast .

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

Это не то, для чего virtual.

virtual есть, чтобы включить полиморфное поведение.По сути, чтобы включить это:

struct A {virtual void foo()=0;};

// Two different "behaviors" for the same "A"
struct B {void foo() override{}};
struct C {void foo() override{}};

// forgive the leak, this is just to prove a point.
A* b = new B();
A* c = new C();
b->foo(); // Will call B::foo, even though this is a pointer to "A"
c->foo(); // Will call C::foo, even though this is a pointer to "A"

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

Если вы хотите убедиться, что B реализует некоторый интерфейс, просто используйте этот интерфейсгде-то.Если B не реализует его, вы получите искомую ошибку компилятора:

class B {};

template<typename T> void call_foo(T* v1, T* v2) {
    v1->foo(&v2);
}

B b1;
B b2;
b1.foo(&b2); // error
call_foo(&b1, &b2); // error

Затем, чтобы избавиться от ошибки, вы можете просто реализовать функцию.Нет virtual необходимо:

class B {
    void foo(B*) {/*do something*/}
};

B b1;
B b2;
b1.foo(&b2); // ok
call_foo(&b1, &b2); // ok


Но почему я не могу использовать виртуальную функцию для этого?

Представьте себе следующий сценарий:

struct A {virtual void foo(A*)=0;};

// Imagine if the language allowed this:
struct B {void foo(B*) override{}};
struct C {void foo(C*) override{}};

// (...)

// I create a vector of objects, and insert three of them in this vector.
std::vector<A*> objects;

// Note that foo is well-defined only for the first two.
objects.push_back(new B();)
objects.push_back(new B();)
objects.push_back(new C();)

// Then I shuffle the vector
std::shuffle(objects.begin(), objects.end());

// At least one of these three lines should give a compiler error.
// Which one(s)?
objects[0]->foo(objects[1]);
objects[0]->foo(objects[2]);
objects[1]->foo(objects[2]);


Но мне нужно, чтобы функция была виртуальной, и мне нужна безопасность типов!

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

...