根据模板sizeof()放置新的

时间:2013-06-08 05:39:08

标签: c++ templates c++11 signals placement-new

这在c ++ 11中是否合法?使用最新的英特尔编译器进行编译并且似乎可以正常工作,但我只是觉得这是一个侥幸。

 class cbase
        {
        virtual void call();
        };

template<typename T> class functor : public cbase
    {
    public:
        functor(T* obj, void (T::*pfunc)())
            : _obj(obj), _pfunc(pfunc) {}

        virtual void call()
            {
            (_obj)(*_pfunc)();
            }
    private:
        T& _obj;
        void (T::*_pfunc)();            
        //edited: this is no good:
        //const static int size = sizeof(_obj) + sizeof(_pfunc);
    };

class signal
    {
    public:
        template<typename T> void connect(T& obj, void (T::*pfunc)())
            {
            _ptr = new (space) functor<T>(obj, pfunc);
            }
    private:
        cbase* _ptr;
        class _generic_object {};
        typename aligned_storage<sizeof(functor<_generic_object>), 
            alignment_of<functor<_generic_object>>::value>::type space;
        //edited: this is no good:
        //void* space[(c1<_generic_object>::size / sizeof(void*))];

    };

具体来说,我想知道void* space[(c1<_generic_object>::size / sizeof(void*))];是否真的要为c1的成员对象(_obj和_pfunc)提供正确的大小。 (事实并非如此)。

编辑: 因此,经过一些更多的研究后,似乎以下(更多?)正确:

typename aligned_storage<sizeof(c1<_generic_object>), 
    alignment_of<c1<_generic_object>>::value>::type space;

然而,在检查生成的程序集时,使用带有此空间的placement new似乎会阻止编译器优化掉对“new”的调用(这似乎是在使用常规'_ptr = new c1;'

时发生的

EDIT2:更改了代码,使意图更加清晰。

3 个答案:

答案 0 :(得分:3)

const static int size = sizeof(_obj) + sizeof(_pfunc);将给出成员大小的总和,但这可能与包含这些成员的类的大小不同。编译器可以在成员之间或最后一个成员之后自由插入填充。因此,将成员的大小加在一起近似于该对象可能的最小值,但不一定给出具有这些成员的对象的大小。

实际上,对象的大小不仅取决于其成员的类型,还取决于它们的顺序。例如:

struct A { 
    int a;
    char b;
};

VS

struct B { 
    char b;
    int a;
};

在许多情况下,A会小于B。在A中,ab之间通常没有填充,但在B中,通常会有一些填充(例如,使用4字节的int, ba之间通常会有3个字节的填充。

因此,您的space可能没有足够的空间来容纳您尝试在init创建的对象。

答案 1 :(得分:1)

我觉得你很幸运;杰里的回答指出可能存在填充问题。我认为你有一个非虚拟类(即没有vtable),基本上有两个指针(在引擎盖下)。

除此之外,算术:(c1<_generic_object>::size / sizeof(void*))存在缺陷,因为如果size sizeof(void *)的倍数,它将被截断。你需要这样的东西:

((c1<_generic_object>::size + sizeof(void *) - 1) / sizeof(void *))

答案 2 :(得分:1)

这段代码甚至没有填充问题,因为它有一些更直接的问题。

模板类c1定义为包含引用类型的成员T &_obj。将sizeof应用于_obj范围内的c1将评估为T的大小,而不是参考成员本身的大小。无法在C ++中获得引用的物理大小(至少直接)。同时,c1<T>类型的任何实际对象在物理上都包含对T的引用,这通常在诸如“引擎盖下”的指针的情况下实现。

由于这个原因,我完全不清楚为什么c1<_generic_object>::size的值被用来作为c1<T>类型的实际对象的步调构造所需的内存量度(对于任何{{} 1}})。它没有任何意义。这些尺寸根本不相关。

纯粹的运气,空类T的大小可能会评估为与参考成员的物理实现的大小相同(或更大)的值。在这种情况下,代码将分配足够的内存量。有人甚至可能会声称_generic_object平等将“通常”在实践中持有。但这只是一个完全随意的巧合,没有任何有意义的基础。

这甚至看起来像是为了纯粹混淆而故意插入代码中的红鲱鱼。

P.S。在GCC sizeof(_generic_object) == sizeof(void *)中,空类实际上评估为sizeof,而不是任何“对齐”大小。这意味着保证上述技术使用太小的值初始化1。更具体地说,在32位GCC中,c1<_generic_object>::size的值将为c1<_generic_object>::size,而任何9的实际大小将为c1<some_type_t>个字节。