微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

有没有办法将 reinterpret_cast 重新解释为虚拟派生 * 并从父级调用覆盖?

如何解决有没有办法将 reinterpret_cast 重新解释为虚拟派生 * 并从父级调用覆盖?

#include <iostream>

template<typename T>
struct printer {

    virtual const T* get(size_t& sz) const = 0;

    void print() {
        size_t sz;
        const T* _t = get(sz); //tries to access 'this'
        for (size_t t = 0; t < sz; t++)
            std::cout << _t[t] << std::endl;
    }
};

template<typename T,size_t sz>
struct mask_t : public printer<T> {
    T data[sz];

    virtual const T* get(size_t& _sz) const override {
        _sz = sz;
        return data;
    }
};

int main(int argc,char** argv) {

    char* buffer = new char[1024];
    {
        using mask_f = mask_t<float,12>;
        using mask_i = mask_t<int,12>;

        mask_f* mf = reinterpret_cast<mask_f*>(buffer + 42);
        mask_i* mi = reinterpret_cast<mask_i*>(buffer + 42);

        //'this' is uninitialized
        mf->print();
        mi->print();

        mask_f _mf = *mf;
        mask_i _mi = *mi;

        //ok
        _mf.print();
        _mi.print();
    }
    delete[] buffer;

    return 0;
}

print()调用 this 时尝试访问 get() 是因为 vfptr 查找吗? 换句话说,这是不可能的吗?

编辑:我知道我可以使用 mask_t 创建一个新的 new 或者像我在这里所做的那样通过取消引用指针。然后定义了 mask_t::this

我不想创建实例的原因是性能问题[这在我承认的示例中不可见]。如果你想回答,请解决这篇文章中唯一的问题。

解决方法

无论类型如何,这都不是有效代码。在 C++ 中,你不能只是将一个随机指针投射到一个对象上并假装它存在。

是的,在 C++20 中,它们确实允许您在某些情况下这样做。但即便如此,这些情况也不包括对具有 virtual 成员函数的类型的操作(因为它们不够简单)。

只需使用placement-new 来构造对象。这就是应该在存储中创建对象的方式。


print() 在调用 get() 时尝试访问它是因为 vfptr 查找吗?

有关系吗?这是未定义行为如何导致崩溃的一个实现方面。

这:mask_f* mf = reinterpret_cast<mask_f*>(buffer + 42); 导致未定义的行为。就像这样:mask_f _mf = *mf;。这两者都访问一个不存在的对象。因此,它们都表现出未定义的行为。

某个特定的编译器(版本)可能会使其中一个看起来像您想要的那样,并可能使另一个崩溃,这是一个细节和实现的问题。这两段代码同样荒谬,都不能依赖于做你想做的事。

我可以解释为什么编译器生成的程序集允许您在一种情况下使用 UB 而不是在另一种情况下。但这忽略了这样一个事实:无论哪种方式,您都依赖 UB。

我正在读取远程进程的内存。

这在 C++ 中是不合理的。无论如何,不​​适用于具有 virtual 成员的类型。传统的方法是将该类型的数据序列化到内存中,然后在接收过程中将其反序列化为该类型的新对象

现在是的,如果您愿意编写特定于平台的hackery,有 方法可以像这样在进程之间传递virtual 类型。它们需要提取 vtable 指针(从有效对象中)并将它们写入从远程进程接收到的对象数据中,从而有效地原位“修复”对象。

但这些是特定于平台的黑客;如果你想要一些可移植的东西,你必须使用序列化。

,

“我知道我可以通过取消引用指针来创建一个新的 mask_t 或者像我在这里所做的那样。然后定义了 mask_t::this。”

这是错误的。当进入其构造函数的主体时,对象开始存在。 new,包括placement new,是一种使构造函数运行的方法。其他方法只是声明一个局部或全局变量。但是,您假设的“取消引用指针”不会神奇地导致构造函数运行并创建对象。如果没有对象,this 就不能指向当前对象。

相反,您会得到未定义行为。任何事情都可能发生。您的硬盘可能会被擦除。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。