如何编写自己的dynamic_cast [英] How to write own dynamic_cast

查看:80
本文介绍了如何编写自己的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 to Base1 does not require any extra work, they are at the same physical address
  • casting from Derived to Base2 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:

    如果 base 不是 Derived 的实例,则
  • dynamic_cast< Derived *>(& base); 返回null.>在这种情况下,
  • dynamic_cast< Derived&>(base); 抛出 std :: bad_cast .
  • dynamic_cast< void *>(base); 返回派生程度最高的类的地址
  • dynamic_cast 遵守访问规范( public ,受保护的和 private 继承)
  • dynamic_cast<Derived*>(&base); returns null if base is not an instance of Derived.
  • dynamic_cast<Derived&>(base); throws std::bad_cast in this case.
  • dynamic_cast<void*>(base); returns the address of the most derived class
  • dynamic_cast respect the access specifications (public, protected and private 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆