如何在没有 RTTI 的情况下设计动态类型系统?
How to design a dynamic type system without the RTTI?
我知道这个问题有点宽泛和不精确,但这就是我想要的:
struct DynTyped { /* HOW? */ };
class Animal : public DynTyped {};
class Dog : public Animal {};
class Cat : public Animal {};
// expect to work
Animal* a1 = new Dog;
Dog* d1 = DOWNCAST<Dog*>(a1);
d1->hello();
// expect to throw
Animal* a2 = new Cat;
Dog* d2 = DOWNCAST<Dog*>(a2);
d2->hello();
我可以做到的一种方法是在基础 class 中设置一个字段。
class Animal {
enum AnimalType { Dog, Cat };
protected:
AnimalType type_;
}
但我想要一种在更高的基础上实现它的方法 class,因为我可能有一个形状基础 class,我不想再做那种类型字段技巧。
它不是完美的替代品,但是,CRTP 具有您想要的潜力...
顺便说一句,你为什么要避免 RTTI?无法模拟 RTTI。
这可能看起来微不足道,但您可以这样做:
class Dog;
class Cat;
struct DynTyped { virtual Dog*AsDog() {return 0;} virtual Cat*AsCat() {return 0;};
class Animal : public DynTyped {};
class Dog : public Animal {virtual Dog*AsDog() {return this;};};
class Cat : public Animal {virtual Cat*AsCat() {return this;};}
首先,您需要向基 class 添加一个虚函数,它将在 运行 时识别对象的实际类型。然后你可以创建一个模板包装函数来封装实际的类型转换。
我的意思是:
class Animal {
public:
virtual int get_Type() const = 0;
};
class Dog : public Animal {
public:
static const int s_Type = 1;
virtual int get_Type() const { return s_Type; }
};
class Cat : public Animal {
public:
static const int s_Type = 2;
virtual int get_Type() const { return s_Type; }
};
template <class T>
T* DynamicCast(Animal* p)
{
return (T::s_Type == p->get_Type()) ? static_cast<T*>(p) : NULL;
}
就是这个主意。您可以使用任何您喜欢的类型来发现实际的对象类型,在这个例子中我使用 int
,您可以为此创建一个 enum
。
另请注意,这适用于类型匹配的情况,相反,如果目标类型是继承的,RTTI 也适用,即源对象可能是派生类型。
之所以如此,是因为 RTTI 信息更复杂,它包含了整个层次结构。
一种方法是使用虚函数进行转换。你可以这样做:
#include <exception>
class Animal;
class Dog;
class Cat;
struct InvalidConversion : public std::exception
{
};
struct DynTyped {
virtual Animal *IsAnimal() { throw InvalidConversion(); }
virtual Dog *IsDog() { throw InvalidConversion(); }
virtual Cat *IsCat() { throw InvalidConversion(); }
};
class Animal : public DynTyped
{
virtual Animal *IsAnimal() override { return this; }
};
class Dog : public Animal
{
virtual Dog *IsDog() override { return this; }
};
class Cat : public Animal
{
virtual Cat *IsCat() override { return this; }
};
然后使用它:
// expect to work
Animal* a1 = new Dog;
Dog* d1 = a1->IsDog();
d1->hello();
// expect to throw
Animal* a2 = new Cat;
Dog* d2 = a2->IsDog();
d2->hello();
我知道这个问题有点宽泛和不精确,但这就是我想要的:
struct DynTyped { /* HOW? */ };
class Animal : public DynTyped {};
class Dog : public Animal {};
class Cat : public Animal {};
// expect to work
Animal* a1 = new Dog;
Dog* d1 = DOWNCAST<Dog*>(a1);
d1->hello();
// expect to throw
Animal* a2 = new Cat;
Dog* d2 = DOWNCAST<Dog*>(a2);
d2->hello();
我可以做到的一种方法是在基础 class 中设置一个字段。
class Animal {
enum AnimalType { Dog, Cat };
protected:
AnimalType type_;
}
但我想要一种在更高的基础上实现它的方法 class,因为我可能有一个形状基础 class,我不想再做那种类型字段技巧。
它不是完美的替代品,但是,CRTP 具有您想要的潜力...
顺便说一句,你为什么要避免 RTTI?无法模拟 RTTI。
这可能看起来微不足道,但您可以这样做:
class Dog;
class Cat;
struct DynTyped { virtual Dog*AsDog() {return 0;} virtual Cat*AsCat() {return 0;};
class Animal : public DynTyped {};
class Dog : public Animal {virtual Dog*AsDog() {return this;};};
class Cat : public Animal {virtual Cat*AsCat() {return this;};}
首先,您需要向基 class 添加一个虚函数,它将在 运行 时识别对象的实际类型。然后你可以创建一个模板包装函数来封装实际的类型转换。
我的意思是:
class Animal {
public:
virtual int get_Type() const = 0;
};
class Dog : public Animal {
public:
static const int s_Type = 1;
virtual int get_Type() const { return s_Type; }
};
class Cat : public Animal {
public:
static const int s_Type = 2;
virtual int get_Type() const { return s_Type; }
};
template <class T>
T* DynamicCast(Animal* p)
{
return (T::s_Type == p->get_Type()) ? static_cast<T*>(p) : NULL;
}
就是这个主意。您可以使用任何您喜欢的类型来发现实际的对象类型,在这个例子中我使用 int
,您可以为此创建一个 enum
。
另请注意,这适用于类型匹配的情况,相反,如果目标类型是继承的,RTTI 也适用,即源对象可能是派生类型。 之所以如此,是因为 RTTI 信息更复杂,它包含了整个层次结构。
一种方法是使用虚函数进行转换。你可以这样做:
#include <exception>
class Animal;
class Dog;
class Cat;
struct InvalidConversion : public std::exception
{
};
struct DynTyped {
virtual Animal *IsAnimal() { throw InvalidConversion(); }
virtual Dog *IsDog() { throw InvalidConversion(); }
virtual Cat *IsCat() { throw InvalidConversion(); }
};
class Animal : public DynTyped
{
virtual Animal *IsAnimal() override { return this; }
};
class Dog : public Animal
{
virtual Dog *IsDog() override { return this; }
};
class Cat : public Animal
{
virtual Cat *IsCat() override { return this; }
};
然后使用它:
// expect to work
Animal* a1 = new Dog;
Dog* d1 = a1->IsDog();
d1->hello();
// expect to throw
Animal* a2 = new Cat;
Dog* d2 = a2->IsDog();
d2->hello();