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