c++ - 调用基类转换为派生类的函数

标签 c++ polymorphism

假设我有下面的代码,用起来安全吗?

基类:

class B
{
    public:
    B(bool isDerived = false) : m_isDerived(isDerived) {}
    bool isDerived() { return m_isDerived; }

    private:
    bool m_isDerived;
}

派生类:

class D : public B
{
    public:
    D() : B(true) {}
}

代码:

B* b = new B(); // Create new base class
D* unknown = static_cast<D*>(b); // Cast the base class into a derived class

if (unknown->isDerived()) // Is this allowed?
    // unknown is a D and can be used as such
else
    // unknown is not a D and can not be used

我可以安全地调用 unknown->isDerived() 即使在这种情况下 unknown 实际上是 B 吗?

我们假设 unknown 永远不会包含 B* 或 D* 以外的任何东西,并且我们永远不会对 unknown 做任何事情,直到 isDerived() 被检查。

编辑:

鉴于这些问题,我将尝试解释我尝试这样做的原因: 所以基本上我有一个 Windows 树控件,它当然不能直接连接到我用来存储数据的 C++ 树结构。因此,我必须将我的数据重新解释为一个 DWORD_PTR,该 DWORD_PTR 与树控件中的每个节点一起存储,因此我在两者之间建立了联系。我的树结构由基本类型(普通节点)或派生类型(包含更多信息的节点,应该以不同方式处理)组成。指向这些的指针是 reinterpret_cast:ed 并放在树控件中。

现在,当我单步执行树控件时,我想对派生类型的节点进行操作,因此我想将 DWORD_PTR 重新解释为派生类型。但为了完全正确,我应该先将它重新解释为基本类型(我猜?),然后如果它是派生类型,则将其向下转换为派生类型。但是我认为我可以通过立即将其重新解释为派生类型并通过函数检查它是否真的是派生类型来使其更简单一些。如果不是,我什么都不做。在我看来,无论派生多少,指针中的基类数据都应该位于相同的内存位置,但我可能错了,这就是我在这里问的原因。

我想通过不涉及 Windows 来使问题更清楚,但实际上我想要的更接近于此:

B* b1 = new B();
B* b2 = new D();
DWORD_PTR ptr1 = reinterpret_cast<DWORD_PTR>(b1);
DWORD_PTR ptr2 = reinterpret_cast<DWORD_PTR>(b2);

D* unknown = reinterpret_cast<D*>(ptr1 /* or ptr2 */); // Safe?
unknown->isDerived(); // Safe?

基本上,无论我做什么,它在某种程度上仍然是不安全的,因为我必须重新解释_cast 数据。

最佳答案

Can I safely call unknown->isDerived() even though unknown is really a B in this case?

首先,你为什么要这样做?您可以只调用 b->isDerived() 然后进行向下转换。

现在,虽然过早且可能无效的向下转换会产生未定义的行为(并且应该被普遍鄙视),但在这种情况下它应该可以工作。 B 和 D 都没有可能改变 m_isDerived 的相对偏移量的隐式数据成员,并且 isDerived 成员函数的地址是常量。

是的,它应该 有效。如果应该对您来说足够好。

编辑:您可以进行一些测试以确保偏移量相同:

#include <cstddef> // for offsetof macro
#include <cassert> // for assert

#define offsetofclass(base, derived) ((static_cast<base*>((derived*)8))-8)

class Test
{
public:
    Test()
    {
        assert(offsetofclass(B, D) == 0);

        // you'll need to befriend Test with B & D to make this work
        // or make the filed public... or just move these asserts
        // to D's constructor
        assert(offsetof(B, m_isDerived) == offsetof(D, m_isDerived));
    }
};
Test g_test;

这将在启动时执行。我不认为它可以变成静态断言(执行和编译时)。

关于c++ - 调用基类转换为派生类的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24200958/

相关文章:

c++ - 追加到 std::array

c++ - 在 C++ 哈希表代码中返回空迭代器

c++ - C++ 中的组件/策略模式、设计和实现

c# - 运算符重载多态返回泛型集合

python - 如何将 post_save 接收器与 django-polymorphic 一起使用?

c++ - 动态分配还是浪费内存?

c++ - 通过模板成员函数访问私有(private)元组元素

c++ - 释放资源的异常处理的替代方案

java - 为什么我的 XML 架构无法验证?

java - 如何知道是编译时多态还是运行时多态?