如何编写自己的dynamic_cast [英] How to write own dynamic_cast
问题描述
这是采访中问到的.
如何编写自己的dynamic_cast.我认为是基于typeid的名称函数.
How to write own dynamic_cast. I think, on the basis of typeid's name function.
现在如何实现自己的伤寒?我对此一无所知.
Now how to implement own typid? I have no clue on it.
推荐答案
这是您没有任何线索的原因, dynamic_cast
和 static_cast
与 const_cast
或 reinterpret_cast
,它们实际上执行指针算术,并且在某种程度上是类型安全的.
There is a reason you don't have any clue, dynamic_cast
and static_cast
are not like const_cast
or reinterpret_cast
, they actually perform pointer arithmetic and are somewhat typesafe.
指针算法
为了说明这一点,请考虑以下设计:
In order to illustrate this, think of the following design:
struct Base1 { virtual ~Base1(); char a; };
struct Base2 { virtual ~Base2(); char b; };
struct Derived: Base1, Base2 {};
Derived
的实例应如下所示(它基于gcc,因为它实际上是依赖于编译器的...):
An instance of Derived
should look something like this (it's based on gcc since it is actually compiler dependent...):
| Cell 1 | Cell 2 | Cell 3 | Cell 4 |
| vtable pointer | a | vtable pointer | b |
| Base 1 | Base 2 |
| Derived |
现在想想铸造所需的工作:
Now think of the work necessary for casting:
- 从
派生
到Base1
的广播不需要任何额外的工作,它们位于相同的物理地址 - 从
派生
到Base2
的广播需要将指针移动2个字节
- casting from
Derived
toBase1
does not require any extra work, they are at the same physical address - casting from
Derived
toBase2
necessitates to shift the pointer by 2 bytes
因此,有必要知道对象的内存布局,以便能够在一个派生对象与其基对象之一之间进行转换.而且这只有编译器才知道,该信息无法通过任何API进行访问,未经标准化或其他任何处理.
Therefore, it is necessary to know the memory layout of the objects to be able to cast between one derived object and one of its base. And this is only known to the compiler, the information is not accessible through any API, it's not standardised or anything else.
在代码中,这将翻译为:
In code, this would translate like:
Derived derived;
Base2* b2 = reinterpret_cast<Base2>(((char*)&derived) + 2);
当然,这是针对 static_cast
的.
现在,如果您能够在 dynamic_cast
的实现中使用 static_cast
,那么您可以利用编译器并让它为您处理指针算术...但是您仍然不失为一种成功.
Now, if you were able to use static_cast
in the implementation of dynamic_cast
, then you could leverage the compiler and let it handle the pointer arithmetic for you... but you're still not out of the wood.
正在编写dynamic_cast吗?
首先,我们需要阐明 dynamic_cast
的规范:
First things first, we need to clarify the specifications of dynamic_cast
:
- 如果
-
dynamic_cast< Derived *>(& base);
返回null.>在这种情况下, -
dynamic_cast< Derived&>(base);
抛出std :: bad_cast
. -
dynamic_cast< void *>(base);
返回派生程度最高的类的地址 -
dynamic_cast
遵守访问规范(public
,受保护的和private
继承)
base
不是 Derived
的实例,则dynamic_cast<Derived*>(&base);
returns null ifbase
is not an instance ofDerived
.dynamic_cast<Derived&>(base);
throwsstd::bad_cast
in this case.dynamic_cast<void*>(base);
returns the address of the most derived classdynamic_cast
respect the access specifications (public
,protected
andprivate
inheritance)
我不认识你,但是我认为这将是丑陋的.在这里使用 typeid
是不够的:
I don't know about you, but I think it's going to be ugly. Using typeid
is not sufficient here:
struct Base { virtual ~Base(); };
struct Intermediate: Base {};
struct Derived: Base {};
void func()
{
Derived derived;
Base& base = derived;
Intermediate& inter = dynamic_cast<Intermediate&>(base); // arg
}
这里的问题是 typeid(base)== typeid(Derived)!= typeid(Intermediate)
,所以您也不能依赖它.
The issue here is that typeid(base) == typeid(Derived) != typeid(Intermediate)
, so you can't rely on that either.
另一个有趣的事情:
struct Base { virtual ~Base(); };
struct Derived: virtual Base {};
void func(Base& base)
{
Derived& derived = static_cast<Derived&>(base); // Fails
}
当涉及到虚拟继承时,
static_cast
不起作用...因此我们遇到了指针算术运算潜入的问题.
static_cast
doesn't work when virtual inheritance is involved... so we've go a problem of pointer arithmetic computation creeping in.
几乎是解决方案
class Object
{
public:
Object(): mMostDerived(0) {}
virtual ~Object() {}
void* GetMostDerived() const { return mMostDerived; }
template <class T>
T* dynamiccast()
{
Object const& me = *this;
return const_cast<T*>(me.dynamiccast());
}
template <class T>
T const* dynamiccast() const
{
char const* name = typeid(T).name();
derived_t::const_iterator it = mDeriveds.find(name);
if (it == mDeriveds.end()) { return 0; }
else { return reinterpret_cast<T const*>(it->second); }
}
protected:
template <class T>
void add(T* t)
{
void* address = t;
mDerived[typeid(t).name()] = address;
if (mMostDerived == 0 || mMostDerived > address) { mMostDerived= address; }
}
private:
typedef std::map < char const*, void* > derived_t;
void* mMostDerived;
derived_t mDeriveds;
};
// Purposely no doing anything to help swapping...
template <class T>
T* dynamiccast(Object* o) { return o ? o->dynamiccast<T>() : 0; }
template <class T>
T const* dynamiccast(Object const* o) { return o ? o->dynamiccast<T>() : 0; }
template <class T>
T& dynamiccast(Object& o)
{
if (T* t = o.dynamiccast<T>()) { return t; }
else { throw std::bad_cast(); }
}
template <class T>
T const& dynamiccast(Object const& o)
{
if (T const* t = o.dynamiccast<T>()) { return t; }
else { throw std::bad_cast(); }
}
您需要在构造函数中添加一些小东西:
You need some little things in the constructor:
class Base: public Object
{
public:
Base() { this->add(this); }
};
所以,让我们检查一下:
So, let's check:
- 经典用途:好吧
-
虚拟
继承?它应该可以工作...但未经测试 - 尊重访问说明... ARG:/
- classic uses: okay
virtual
inheritance ? it should work... but not tested- respecting access specifiers... ARG :/
祝所有尝试在编译器之外实现此功能的人都好运:x
Good luck to anyone trying to implement this outside of the compiler, really :x
这篇关于如何编写自己的dynamic_cast的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!