动态铸造的替代方案
Alternative to dynamic casting
在C++中使用dynamic_cast
有没有其他方法?
例如,在下面的代码中,我希望能够让Cat
对象发出咕噜声。但只能Cat
对象,而不是Dog
对象。我知道这违背了从Mammal
派生类,因为它不是很多态,但我仍然想知道我是否可以在没有dynamic_cast
的情况下做到这一点。
我的类声明
class Mammal
{
public:
virtual void Speak() const {cout << "Mammals yay!n";}
};
class Cat: public Mammal
{
public:
void Speak() const{cout << "Meown";}
void Purr() const {cout <"rrrrrrrrn";}
};
class Dog: public Mammal
{
public:
void Speak() const{cout << "Woof!n";}
};
在主
int main()
{
Mammal *pMammal;
pMammal = new Cat;
pMammal->Purr(); //How would I call this without having to use dynamic_cast?
return 0;
}
如果您知道有一组固定的实现,则可以创建virtual
个函数来为您执行强制转换。 这可以比dynamic_cast
便宜。
所以:
struct Cat;
struct Mammal {
virtual Cat* AsCat(){ return nullptr; }
};
struct Cat : Mammal {
virtual Cat* AsCat() { return this; }
};
我在C++11中用template
愚蠢的方式做到了这一点,所以你甚至可以让它看起来像一个演员。
#include <utility>
#include <iostream>
template<typename T>
struct fast_castable_leaf {
virtual T* do_fast_cast(T* unused=nullptr) { return nullptr; }
virtual T const* do_fast_cast(T* unused=nullptr) const { return nullptr; }
virtual ~fast_castable_leaf() {}
};
template<typename Tuple>
struct fast_castable;
template<template<typename...>class Tuple>
struct fast_castable<Tuple<>> {
virtual ~fast_castable() {}
};
template<template<typename...>class Tuple, typename T, typename... Ts>
struct fast_castable<Tuple<T,Ts...>>:
fast_castable_leaf<T>,
fast_castable<Tuple<Ts...>>
{};
template<typename T> struct block_deduction { typedef T type; };
template<typename T> using NoDeduction = typename block_deduction<T>::type;
template<typename T>
T* fast_cast( NoDeduction<fast_castable_leaf<T>>* src ) {
return src->do_fast_cast();
}
template<typename T>
T const* fast_cast( NoDeduction<fast_castable_leaf<T>> const* src ) {
return src->do_fast_cast();
}
template<typename T, typename D>
struct fast_cast_allowed : std::integral_constant<bool,
std::is_base_of<T,D>::value || std::is_same<T,D>::value
> {};
template<typename D, typename B, typename Tuple>
struct implement_fast_cast;
template<typename D, typename B, template<typename...>class Tuple>
struct implement_fast_cast<D,B,Tuple<>> : B {};
template<typename D, typename B, template<typename...>class Tuple, typename T, typename... Ts>
struct implement_fast_cast<D,B,Tuple<T,Ts...>> : implement_fast_cast<D, B, Tuple<Ts...>> {
private:
D* do_cast_work(std::true_type) { return static_cast<D*>(this); }
D const* do_cast_work(std::true_type) const { return static_cast<D const*>(this); }
std::nullptr_t do_cast_work(std::false_type) { return nullptr; }
std::nullptr_t do_cast_work(std::false_type) const { return nullptr; }
public:
T* do_fast_cast( T* unused = nullptr ) override { return do_cast_work( fast_cast_allowed<T,D>() ); }
T const* do_fast_cast( T* unused = nullptr ) const override { return do_cast_work( fast_cast_allowed<T,D>() ); }
};
以及正在使用的上述框架的示例:
struct Dog;
struct Cat;
struct Moose;
template<typename...>struct Types {};
typedef Types<Dog, Cat, Moose> Mammal_Types;
// A Mammal can be fast-casted to any of the Mammal_Types:
struct Mammal : fast_castable<Mammal_Types>
{};
// Cat wants to implement any legal fast_casts it can for Mammal in the
// set of Mammal_Types. You can save on overhead by doing Types<Cat> instead
// of Mammal_Types, but this is less error prone:
struct Cat : implement_fast_cast< Cat, Mammal, Mammal_Types >
{};
int main() {
Cat c;
Mammal* m=&c;
// so m is a pointer to a cat, but looks like a mammal. We use
// fast cast in order to turn it back into a Cat:
Cat* c2 = fast_cast<Cat>(m);
// and we test that it fails when we try to turn it into a Dog:
Dog* d2 = fast_cast<Dog>(m);
// This prints out a pointer value for c2, and 0 for d2:
std::cout << c2 << "," << d2 << "n";
}
现场示例
这可以清理以支持更标准的fast_cast<Cat*>
而不是fast_cast<Cat>
,以及fast_cast<Cat&>
,然后通过将其设为私有并fast_cast
friend
来阻止对do_fast_cast
的直接访问,并允许一些方法在您需要的情况下具有virtual
继承。
但系统的核心在上面。 您可以以单个virtual
函数查找为代价进行铸造派生,而无需自己维护大部分机器。
替代实现:
template<class...>struct types{using type=types;};
template<typename T>
struct fast_castable_leaf {
virtual T* do_fast_cast(T* unused=nullptr) { return nullptr; }
virtual T const* do_fast_cast(T* unused=nullptr) const { return nullptr; }
virtual ~fast_castable_leaf() {}
};
template<class Tuple>
struct fast_castable;
template<>
struct fast_castable<types<>> {
virtual ~fast_castable() {}
};
template<class T0, class...Ts>
struct fast_castable<types<T0, Ts...>>:
fast_castable_leaf<T0>,
fast_castable<types<Ts...>>
{};
template<class T> struct block_deduction { typedef T type; };
template<class T> using NoDeduction = typename block_deduction<T>::type;
template<class T>
T* fast_cast( NoDeduction<fast_castable_leaf<T>>* src ) {
return src->do_fast_cast();
}
template<class T>
T const* fast_cast( NoDeduction<fast_castable_leaf<T>> const* src ) {
return src->do_fast_cast();
}
template<class T, class D>
struct fast_cast_allowed : std::integral_constant<bool,
std::is_base_of<T,D>::value || std::is_same<T,D>::value
> {};
template<class Self, class Base, class Types>
struct implement_fast_cast;
template<class Self, class Base>
struct implement_fast_cast<Self,Base,types<>> : Base {
private:
template<class, class, class>
friend struct implement_fast_cast;
Self* do_cast_work(std::true_type) { return static_cast<Self*>(this); }
Self const* do_cast_work(std::true_type) const { return static_cast<Self const*>(this); }
std::nullptr_t do_cast_work(std::false_type) { return nullptr; }
std::nullptr_t do_cast_work(std::false_type) const { return nullptr; }
};
template<class Self, class Base, class T0, class... Ts>
struct implement_fast_cast<Self,Base,types<T0,Ts...>> :
implement_fast_cast<Self, Base, types<Ts...>>
{
public:
T0* do_fast_cast( T0* unused = nullptr ) override { return this->do_cast_work( fast_cast_allowed<T0,Self>() ); }
T0 const* do_fast_cast( T0* unused = nullptr ) const override { return this->do_cast_work( fast_cast_allowed<T0,Self>() ); }
};
struct Dog;
struct Cat;
struct Moose;
typedef types<Dog, Cat, Moose> Mammal_Types;
struct Mammal : fast_castable<Mammal_Types>
{};
struct Cat : implement_fast_cast< Cat, Mammal, Mammal_Types >
{};
int main() {
Cat c;
Mammal* m=&c;
Cat* c2 = fast_cast<Cat>(m);
Dog* d2 = fast_cast<Dog>(m);
std::cout << c2 << "," << d2 << "n";
}
对于某些编译器来说,这可能更容易接受。 活生生的例子。
请注意,对于一长串类型,上面的内容变得笨拙(在编译时和可能的运行时),因为它依赖于线性继承。
二进制继承系统的编程会稍微复杂一些,但可以解决这个问题。 在其中,您将要继承的内容列表拆分为两个大小相等的列表,并从两者继承。 实现快速转换必须通过虚拟中介从Base
继承。
C++不支持像Objective C或Smalltalk那样发送消息。若要调用方法,需要为支持该方法的对象具有静态类型句柄。您是否需要使用dynamic_cast<Cat*>(pointer)
或者是否可以摆脱其他事情,例如static_cast<Cat*>(pointer)
是一个单独的问题。
由于dynamic_cast<...>()
相对昂贵,并且尝试可能无限数量的不同类是不可行的,因此最好在基类中使用visit()
方法,该方法由访问者调用。但是,这些只是获取正确类型引用的技术。
您正在处理指向类型Mammal
的指针,大概Mammal
没有定义Purr()
。您绝对必须将其转换为键入Cat
的指针才能访问Purr()
。你可以用C型的演员表或dynamic_cast
来做到这一点,后者通常是C++中更合适的做法。dynamic_cast
还有一个优点,您可以使用它在运行时测试您的Mammal
对象是否为Cat
,因此您可以决定是否可以调用Purr()
。
三种变体:
-
dynamic_cast,你应该已经知道了。
-
static_cast,即编译时强制转换,即 a) 检查类型的兼容性 b) 计算基类和派生类之间的偏移量并考虑在内 c) 没有运行时检查。
-
reinterpret_cast,也是编译时强制转换,无需任何类型检查和偏移量计算即可完成。谨慎使用 - 此强制转换可能会导致非常难以调试的错误。
有关这些演员的完整参考和示例,请查找一些书籍和教程,例如:http://www.cplusplus.com/doc/tutorial/typecasting/
- 运行同一解决方案的另一个项目的项目
- std::向量与传递值的动态数组
- 在c++中用vector填充一个简单的动态数组
- 自上而下的动态规划与递归朴素解决方案.检查运行时执行
- C++中TSP的动态编程解决方案
- 向一系列指针及其替代方案的动态分配
- 我该如何判断k-server动态解决方案的最佳路径以数组成本[i] [j] [k] [t]位于何处
- 具有模板化类型的动态类型Singleton.这是可行的方法吗?[提供的解决方案]
- 迭代解决方案作为动态编程
- 动态铸造的替代方案
- 如何验证竞争性编程任务的动态编程解决方案的正确性
- 可调整大小的动态二维阵列的更好解决方案
- 用于动态分配数组的unique_ptr替代方案
- 具有对齐成员的对象的动态分配-可能的解决方案
- 递归解决方案的动态编程解决方案
- 使用动态编程进行更改的所有解决方案
- 动态规划解决方案-如何解决?(给我指个正确的方向)
- 针对不相关类型的动态调度的解决方案
- 使用IDispatch的c++和COM的类似动态的替代方案
- 动态内存分配:当最右边的维度是可变的时,二维数组的替代方案是什么