Скажем, я хочу перегрузить функцию за пределами моих классов разными типами указателей. Могу ли я сделать это на С++ 11?
struct Bird;
struct Bear;
struct Animal {
virtual Bird* AsBird() = 0;
virtual Bear* AsBear() = 0;
};
struct Bird : public Animal{
virtual Bird* AsBird(){ return this; }
virtual Bear* AsBear(){ return NULL; }
};
struct Bear : public Animal{
virtual Bird* AsBird(){ return NULL; }
virtual Bear* AsBear(){ return this; }
};
void Print(Animal* a){
cout << "I don't know what animal this is!" << endl;
}
void Print(Bear* b){
cout << "That a bear!" << endl;
}
void Print(Bird* b){
cout << "That a bird!" << endl;
}
int main(int argc, char* argv[]){
Animal* a = new Bear;
Bear* bear;
Bird* bird;
if (bear = a->AsBear()){
Print(bear);
} else if (bird = a->AsBird()){
Print(bird);
}
return 0;
}
Этот код работает, но он абсолютно ужасен. Я пробовал играть с шаблонами и авто, но компилятор не хочет иметь ничего общего с моими злыми экспериментами. Есть ли законный способ сделать это?
То, что вы сделали, - это перегрузка функции Print
free, изменяя тип ее параметра, нет наследования, и это совершенно законно.
Но вам это не нужно (или любое подобное dynamic_cast-like, как и вы): то, что вам нужно сделать, это добавить virtual void Print() const = 0
в ваш базовый класс Animal
и переопределить его в каждом производном классе.
Пример:
struct Animal {
virtual void Print() const = 0;
};
struct Bird : public Animal{
void Print() const { cout << "That a bird!\n"; }
};
struct Bear : public Animal{
void Print() const { cout << "That a bear!\n"; }
};
int main(){
Animal* a = new Bear;
a->Print();
Animal* b = new Bird;
b->Print();
}
Это выглядит как пример использования шаблона посетителя.
struct Bird;
struct Bear;
struct Visitor
{
virtual void Visit(Bird& x) = 0;
virtual void Visit(Bear& x) = 0;
};
struct PrintVisitor : Visitor
{
void Visit(Bird& x) override { cout << "That a bird!" << endl; };
void Visit(Bear& x) override { cout << "That a bear!" << endl; };
};
struct Animal
{
virtual void Accept(Visitor& v) = 0;
};
struct Bird : public Animal
{
void Accept(Visitor& v) override { v.Visit(*this); }
};
struct Bear : public Animal
{
void Accept(Visitor& v) override { v.Visit(*this); }
};
int main(int argc, char* argv[])
{
Bear bear;
Bird bird;
PrintVisitor visitor;
Animal* a = &bear;
a->Accept(visitor);
a = &bird;
a->Accept(visitor);
}
Конечно, проще было бы сделать Print
виртуальной функцией-членом.
Как отметил квантв, традиционный способ заключается в добавлении виртуальной функции в вашу иерархию классов.
Однако, если вы не хотите этого делать, вы можете использовать dynamic_cast
, который был изобретен специально для таких целей.
struct Animal {
virtual ~Animal() {} // base class must have a virtual method to use dynamic_cast
};
...
if (bear = dynamic_cast<Bear*>(a)){
Print(bear);
} else if (bird = dynamic_cast<Bird*>(a)){
Print(bird);
}
Это немного лучше, чем у вас: если вы добавите другой наследующий класс, вам не придется менять свой базовый класс; вы должны только изменить main
функцию и добавить новую функцию печати.
Если это все еще "ужасно", возможно, вы должны сделать Print
виртуальной функцией.
Функции AsBear
, AsBird
, вы просто переопределили dynamic_cast
.
Что касается перегрузок Print
, самое простое решение здесь - сделать virtual
функцию Print
в иерархии Animal
.
Моим рекомендуемым решением было бы использовать шаблон посетителя. С тех пор, как вы пытаетесь сделать это, отделив функцию Print
и иерархию классов.
#include <iostream>
struct Bird;
struct Bear;
struct Animal {
struct Visitor {
virtual void operator()(const Bird *) const = 0;
virtual void operator()(const Bear *) const = 0;
};
virtual void Accept(const Visitor &visitor) const = 0;
};
struct Bird : public Animal {
virtual void Accept(const Visitor &visitor) const override {
visitor(this);
}
};
struct Bear : public Animal {
virtual void Accept(const Visitor &visitor) const override {
visitor(this);
}
};
struct Print : public Animal::Visitor {
virtual void operator()(const Bird *) const override {
std::cout << "Bird" << std::endl;
}
virtual void operator()(const Bear *) const override {
std::cout << "Bear" << std::endl;
}
};
int main() {
Bird bird;
Animal *animal = &bird;
animal->Accept(Print());
}
AsBear()
... вы просто повторно реализуетdynamic_cast
?