C++ переопределить виртуальный метод с абстрактным классом в качестве параметра

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

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 ответа

Решение

Я понимаю, что ваш вопрос больше о синтаксисе. То, что у вас есть, правильно, просто передайте объект типа 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.

Это не то 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, у вас неправильный тип. Затем вы можете сгенерировать исключение или распечатать диагностическое сообщение.

Другие вопросы по тегам