繁体   English   中英

C ++中不同大小的不同行为(Firebreath源代码)

[英]Different behaviors for different size in C++ (Firebreath source code)

当我浏览firebreath的源代码( src / ScriptingCore / Variant.h )时遇到一个困惑的问题

    // function pointer table
    struct fxn_ptr_table {
        const std::type_info& (*get_type)();
        void (*static_delete)(void**);
        void (*clone)(void* const*, void**);
        void (*move)(void* const*,void**);
        bool (*less)(void* const*, void* const*);
    };

    // static functions for small value-types 
    template<bool is_small>
    struct fxns
    {
        template<typename T>
        struct type {
            static const std::type_info& get_type() { 
                return typeid(T); 
            }
            static void static_delete(void** x) { 
                reinterpret_cast<T*>(x)->~T(); 
            }
            static void clone(void* const* src, void** dest) { 
                new(dest) T(*reinterpret_cast<T const*>(src)); 
            }
            static void move(void* const* src, void** dest) { 
                reinterpret_cast<T*>(dest)->~T(); 
                *reinterpret_cast<T*>(dest) = *reinterpret_cast<T const*>(src); 
            }
            static bool lessthan(void* const* left, void* const* right) {
                T l(*reinterpret_cast<T const*>(left));
                T r(*reinterpret_cast<T const*>(right));

                return l < r;
            }
        };
    };

    // static functions for big value-types (bigger than a void*)
    template<>
    struct fxns<false>
    {
        template<typename T>
        struct type {
            static const std::type_info& get_type() { 
                return typeid(T); 
            }
            static void static_delete(void** x) { 
                delete(*reinterpret_cast<T**>(x)); 
            }
            static void clone(void* const* src, void** dest) { 
                *dest = new T(**reinterpret_cast<T* const*>(src)); 
            }
            static void move(void* const* src, void** dest) { 
                (*reinterpret_cast<T**>(dest))->~T(); 
                **reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src); 
            }
            static bool lessthan(void* const* left, void* const* right) {
                return **reinterpret_cast<T* const*>(left) < **reinterpret_cast<T* const*>(right);
            }
        };
    };

    template<typename T>
    struct get_table 
    {
        static const bool is_small = sizeof(T) <= sizeof(void*);

        static fxn_ptr_table* get()
        {
            static fxn_ptr_table static_table = {
                fxns<is_small>::template type<T>::get_type
                , fxns<is_small>::template type<T>::static_delete
                , fxns<is_small>::template type<T>::clone
                , fxns<is_small>::template type<T>::move
                , fxns<is_small>::template type<T>::lessthan
            };
            return &static_table;
        }
    };

问题是,为什么大值类型(大于void *)的静态函数的实现不同于小值类型。

例如,static_delete用于小值类型仅在T实例上调用析构函数,而对于大值类型则使用'delete'。

有什么把戏吗? 提前致谢。

看起来Firebreath为其小型对象使用了专用的内存池,而大型对象通常在堆中分配。 因此,不同的行为。 请注意,例如, clone()中小对象的new放置:这将在指定的内存位置创建新对象而不分配它。 使用new放置创建对象时,必须在取消分配内存之前显式调用该析构函数,这就是static_delete()作用。

内存实际上并没有被释放,因为正如我所说,它似乎正在使用专用内存池。 内存管理必须在其他地方执行。 这种内存池是对小型对象的常见优化。

内部文档怎么说? 如果作者没有记录,则他可能不认识自己。

从代码来看,小对象的接口与大对象的接口不同。 传递给小对象的指针是指向对象本身的指针,传递给大对象的指针是指向对象的指针。

但是,作者似乎不太了解C ++(我会避免使用任何类似的代码)。 例如,在move ,他显式地销毁了对象,然后将其分配给对象:这是保证未定义的行为,对于最简单的内置类型,它可能无法可靠地工作。 同样,小物体与大物体的区别在很大程度上是不相关的。 一些“小”物体复制起来可能会非常昂贵。 当然,考虑到这里的所有内容都是模板,因此绝对没有理由对任何内容使用void*

我已经编辑了您的问题,以包括指向原始源文件的链接,因为显然,在这里回答的大多数人都没有阅读过该文件,以了解实际情况。 我承认这可能是FireBreath中最令人困惑的代码之一; 当时,我试图避免使用Boost,这确实非常有效。

从那以后,我一直考虑切换到boost :: any(对于那些想提出建议的人,不,boost :: variant无法正常工作,我也不会在这里解释原因;请问另一个问题,您是否真的在乎)我们已经自定义了该类的数量,以使其完全满足我们的需要,并增强:::任何人都很难以类似的方式进行自定义。 最重要的是,我们一直在遵循旧的轴芯:如果它没有损坏,请不要修复它!

首先,您应该知道几位C ++专家已经审阅过此代码。 是的,它使用了许多人认为可疑的做法,但经过了仔细考虑,它们在FireBreath支持的编译器上是一致且可靠的。 我们已经使用valgrind,视觉泄漏检测器,LeakFinder和Rational Purify进行了广泛的测试,但从未在此代码中发现任何泄漏。 这有点让人困惑。 令我惊讶的是,那些不懂代码的人以为作者不懂C ++。 在这种情况下,克里斯托弗·迪金斯(克里斯托弗·迪金斯(Christopher Diggins)(他编写了您引用的代码以及原始的cdiggins :: any类,从中获取的任何类)似乎都非常了解C ++,并证明了他能够编写此代码。 该代码在内部使用并经过高度优化-实际上,可能超出了FireBreath的需求。 但是,它对我们很有帮助。

我将尽我所能尽力解释您问题的答案; 请记住,我没有太多时间,因为我真的对此进行了深入研究已经有一段时间了。 使用不同静态类的“小”类型的主要原因是“小”类型几乎是内置类型。 一个int,一个char,一个long等。大于void *的任何值都被视为某种对象。 这是一项优化,允许它尽可能地重用内存,而不是删除和重新分配它。

如果您并排看代码,这将更加清晰。 如果您查看删除和克隆,您会发现在“大”对象上它是动态分配内存的。 它在删除和克隆中调用“删除”,并使用常规的“新”。 在“小型”版本中,它仅在内部存储内存并重复使用; 它从不“删除”内存,它只是在内部拥有的内存上调用析构函数或正确类型的构造函数。 同样,这样做只是为了提高效率。 在两种类型上移动时,它都调用旧对象的析构函数,然后分配新对象数据。

对象本身存储为void *,因为我们实际上并不知道对象将是哪种类型;因此,它实际上是空的。 实际上,要使对象退回,您必须指定类型。 这是允许容器完全容纳任何类型的数据的一部分。 这就是在那里有那么多reinterpret_cast调用的原因-许多人看到了并说:“哦,不!作者一定很笨!” 但是,当您需要取消引用的void *时,这正是您要使用的运算符。

不管怎么说,这些名流实际上已经在今年推出了他任何班级的新版本。 我需要看一下它,并且可能会尝试将其替换为当前版本。 诀窍是我已经定制了当前版本(主要是添加一个比较运算符,以便可以将其放入STL容器中并添加convert_cast),因此我需要确保我对新版本足够了解,以便安全地进行操作。

希望能有所帮助; 我从中得到的文章在这里: http : //www.codeproject.com/KB/cpp/dynamic_typing.aspx

请注意,该文章已更新,似乎无法再使用原始文章了。

编辑

自从我写这篇文章以来,我们已经确认了旧变量类的一些问题,并且已经对其进行了更新并替换为使用boost :: any的类。 感谢dougma所做的大部分工作。 FireBreath 1.7(在撰写本文时为当前主分支)包含该修复程序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM