如何解决Upcasting/Downcasting 以通过基类处理多个派生类
假设我有一个基类和多个派生类,我想避免每个派生类类型都有多个变量/函数。
我发现实现这一点的唯一方法是使用向上/向下转换。但特别是低头通常被认为是不好的做法 - 那么该怎么做呢?
示例:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
class Animal{
protected:
Animal(std::string val) { name = val; }
public:
virtual ~Animal() = default;
std::string name;
};
class Cat: public Animal {
public:
Cat(std::string name) : Animal(name) { }
~Cat() = default;
void purr();
};
class Dog: public Animal {
public:
Dog(std::string name): Animal(name) { }
~Dog() = default;
void bark();
};
void pet(Animal *animal) {
if (!animal)
return;
if (dynamic_cast<Cat*>(animal))
std::cout << "petting my cat " << animal->name << std::endl;
else if (dynamic_cast<Dog*>(animal))
std::cout << "petting my dog " << animal->name << std::endl;
};
int main()
{
Animal *currentPet = new Cat("Lucy");
pet(currentPet);
// after many joyful years - RIP
delete currentPet;
currentPet = new Dog("Diego");
pet(currentPet);
delete currentPet;
currentPet = NULL;
return 0;
}
这完全没问题还是仍然被认为是糟糕的设计/实践?
解决方法
我觉得实现这一点的最佳方法是在基类中有一个虚方法 petted() 并在每个子类中覆盖它以满足该子类的需要。
class Animal {
// ...
virtual void petted() { std::cout << "Petted general animal"; }
}
class Dog {
// ...
void petted() { std::cout << "Petted dog [...]"; }
}
class Cat {
// ...
void petted() { std::cout << "Petted cat [...]"; }
}
// this doesn't even have to be a separate function...
void pet(Animal* a) {
a->petted();
}
使其成为 virtual 将让您通过基类指针(指针指向的实例的实际类型在运行时检查)实现运行时多态性,这将选择 petted() 方法的正确“版本”。
至少我会这样做(到目前为止,我只用 C++ 编写了一年半)。
,更新
添加 OP 请求的示例。另外,我们可以使用 const 引用来代替在 pet 函数中使用原始指针。这将节省您检查空指针的时间。
是的,应该很少使用向下转换。这是一种无需向下转型即可实现您想要的目标的可能实现。
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <memory>
class Animal{
protected:
std::string name_;
std::string type_;
Animal(const std::string& name,const std::string& type) : name_(name),type_(type)
{}
public:
virtual ~Animal() = default;
virtual std::string sound() const = 0;
std::string name() const { return name_; }
std::string type() const { return type_; }
};
class Cat: public Animal {
public:
Cat(std::string name) : Animal(name,"cat") { }
~Cat() = default;
std::string sound() const override { return "Meow"; }
};
class Dog: public Animal {
public:
Dog(std::string name): Animal(name,"dog") { }
~Dog() = default;
std::string sound() const override { return "Wolf"; }
};
void pet(const Animal& animal) {
std::cout << "petting my" << animal.type() << " " << animal.name() << " and I " << animal.sound() << std::endl;
};
int main()
{
std::shared_ptr<Animal> currentPet = std::make_shared<Cat>("Lucy");
pet(*currentPet);
currentPet = std::make_shared<Dog>("Diego");
pet(*currentPet);
return 0;
}
我尽可能保留了您的原始代码,我刚刚删除了向下转换并使用了智能指针。
现在,函数pet不需要知道Animal派生类的任何具体信息,如果你再添加一个派生类,pet函数将保持不变,它会自动处理新的类。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。