Upcasting/Downcasting 以 class 为基础处理多个派生的 classes
Upcasting/Downcasting to handle mutliple derived classes by base class
假设我有一个基础 class 和多个派生 class es,我想避免每个派生 class 类型都有多个 variables/functions。
我发现实现这一点的唯一方法是使用 up-/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;
}
这完全没问题还是仍然被认为是不好的design/practice?
我觉得实现这一目标的最佳方法是在基础 class 中使用虚方法 petted() 并在每个子class 中覆盖它以满足该子[=的需要17=].
class Animal {
// ...
virtual void petted() { std::cout << "Petted general animal"; }
}
class Dog: public Animal {
// ...
void petted() { std::cout << "Petted dog [...]"; }
}
class Cat: public Animal {
// ...
void petted() { std::cout << "Petted cat [...]"; }
}
// this doesn't even have to be a separate function...
void pet(Animal* a) {
a->petted();
}
将其设为虚拟将使您可以通过 base-class 指针实现运行时多态性(在运行时检查指针指向的实例的实际类型),这将选择正确的“版本” 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 的派生 classes 的任何具体信息,如果您添加另一个派生 class pet 函数将保持不变,它将处理新 class 自动。
假设我有一个基础 class 和多个派生 class es,我想避免每个派生 class 类型都有多个 variables/functions。
我发现实现这一点的唯一方法是使用 up-/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;
}
这完全没问题还是仍然被认为是不好的design/practice?
我觉得实现这一目标的最佳方法是在基础 class 中使用虚方法 petted() 并在每个子class 中覆盖它以满足该子[=的需要17=].
class Animal {
// ...
virtual void petted() { std::cout << "Petted general animal"; }
}
class Dog: public Animal {
// ...
void petted() { std::cout << "Petted dog [...]"; }
}
class Cat: public Animal {
// ...
void petted() { std::cout << "Petted cat [...]"; }
}
// this doesn't even have to be a separate function...
void pet(Animal* a) {
a->petted();
}
将其设为虚拟将使您可以通过 base-class 指针实现运行时多态性(在运行时检查指针指向的实例的实际类型),这将选择正确的“版本” 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 的派生 classes 的任何具体信息,如果您添加另一个派生 class pet 函数将保持不变,它将处理新 class 自动。