gpt4 book ai didi

c++ - 为类实现标识符或使用 dynamic_cast

转载 作者:行者123 更新时间:2023-11-30 01:12:29 26 4
gpt4 key购买 nike

我的问题与 What's the point of IsA() in C++? 有关.我有一个性能关键代码,其中包含在特定位置处理来自派生类的特定函数,其中只有基指针可用。检查我们拥有哪个派生类的最佳方法是什么?我编写了两个选项,在第二个选项中我可以消除 Animal_type 枚举和 get_type() 函数。

#include <iostream>

enum Animal_type { Dog_type, Cat_type };

struct Animal
{
virtual Animal_type get_type() const = 0;
};

struct Dog : Animal
{
void go_for_walk() const { std::cout << "Walking. Woof!" << std::endl; }
Animal_type get_type() const { return Dog_type; }
};

struct Cat : Animal
{
void be_evil() const { std::cout << "Being evil!" << std::endl; }
Animal_type get_type() const { return Cat_type; }
};

void action_option1(Animal* animal)
{
if (animal->get_type() == Dog_type)
dynamic_cast<Dog*>(animal)->go_for_walk();
else if (animal->get_type() == Cat_type)
dynamic_cast<Cat*>(animal)->be_evil();
else
return;
}

void action_option2(Animal* animal)
{
Dog* dog = dynamic_cast<Dog*>(animal);
if (dog)
{
dog->go_for_walk();
return;
}

Cat* cat = dynamic_cast<Cat*>(animal);
if (cat)
{
cat->be_evil();
return;
}

return;
}

int main()
{
Animal* cat = new Cat();
Animal* dog = new Dog();

action_option1(cat);
action_option2(cat);

action_option1(dog);
action_option2(dog);

return 0;
}

最佳答案

这在很大程度上取决于您的性能关键代码的性能关键程度。我见过这样的设置,即使是虚拟函数的动态调度也太昂贵了,所以如果你在这样的领域,忘记 dynamic_cast 并手工制作一些东西。

不过,我假设您可以接受一两次虚拟通话。您可能希望避开 dynamic_cast,因为它通常比动态调度慢得多。

现在,您有 N 个派生自公共(public)基类的类和代码中的 M 个点,您需要根据具体的派生类做出决定。问题是:N, M 中的哪一个更有可能在未来发生变化?您是否更有可能添加新的派生类,或在类型决策重要的地方引入新点?此答案将决定最适合您的设计。

如果您要添加新类,但类型区分位置的数量是固定的(理想情况下也很少),枚举方法将是最佳选择。只需使用 static_cast 而不是 dynamic_cast;如果您知道实际的运行时类型,则不需要访问 RTTI 来为您进行转换(除非涉及虚拟基础和更深层次的继承层次结构)。

另一方面,如果列表类是固定的,但可能会引入新的类型区分操作(或者如果它们太多而无法维护),请考虑 Visitor pattern反而。为您的 Animal 类提供一个虚拟的访客接受功能:

virtual void accept(AnimalVisitor &v) = 0;

struct AnimalVisitor
{
virtual void visit(Dog &dog) = 0;
virtual void visit(Cat &cat) = 0;
};

然后,每个派生类都会实现它:

void Dog::accept(AnimalVisitor &v)
{
v.visit(*this);
}

void Cat::accept(AnimalVisitor &v)
{
v.visit(*this);
}

您的操作将只使用它:

void action(Animal *animal)
{
struct Action : AnimalVisitor
{
void visit(Dog &d) override { d.go_for_walk(); }
void visit(Cat &c) override { c.be_evil(); }
};

AnimalVisitor v;

animal->accept(v);
}

如果您要添加新的派生类和新的操作,您可以向上述访问者添加非抽象函数,这样不需要知道新类的现有代码就不会中断:

struct AnimalVisitor
{
virtual void visit(Dog &d) = 0;
virtual void visit(Cat &c) = 0;
virtual void visit(Parrot &p) {}
};

关于c++ - 为类实现标识符或使用 dynamic_cast,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34173816/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com