微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Upcasting/Downcasting 以通过基类处理多个派生类

如何解决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 举报,一经查实,本站将立刻删除。