【问题标题】:Allocation of variable-sized class可变大小类的分配
【发布时间】:2012-07-19 15:50:24
【问题描述】:

我有一个可变长度的数据结构,一个多维迭代器:

class Iterator
{
public:
    static Iterator& init(int dim, int* sizes, void* mem)
    {
        return *(new (mem) Iterator(dim, sizes));
    }

    static size_t alloc_size(int dim)
    {
        return sizeof(Iterator) + sizeof(int) * 2 * dim;
    }

    void operator++()
    {
        // increment counters, update pos_ and done_
    }

    bool done() const { return done_; }
    bool pos()  const { return pos_; }

private:
    Iterator(int dim, int* sizes) : dim_(dim), pos_(0), done_(false)
    {
        for (int i=0; i<dim_; ++i) size(i) = sizes[i];
        for (int i=0; i<dim_; ++i) counter(i) = 0;
    }

    int  dim_;
    int  pos_;
    bool done_;
    int  size   (int i) { return reinterpret_cast<int*>(this+1)[i]; }
    int& counter(int i) { return reinterpret_cast<int*>(this+1)[dim_+i]; }
};

迭代器的维数在编译时是未知的,但可能很小,所以我用alloca为迭代器分配内存:

void* mem = alloca(Iterator::alloc_size(dim));

for (Iterator& i = Iterator::create(dim, sizes, mem); !i.done(); ++i)
{
    // do something with i.pos()
}

有没有更优雅的方式为迭代器分配内存?我知道在从函数返回时,它的堆栈被展开,因此alloca 必须在调用者的堆栈框架中使用(参见例如here)。这个answer 建议在默认参数中执行分配:

static Iterator& init(int dim, int* sizes, void* mem = alloca(alloc_size(dim)));

无论多么优雅,这个解决方案对我没有帮助:Default argument references parameter 'dim'。有什么好的解决方案建议吗?

【问题讨论】:

  • 看到这个我眼睛都疼了。看起来像是无限递归 (bool done() const { return done(); }) 与未定义行为的混合。
  • @TadeuszKopec:呵呵,对,对不起:)我的意思当然是done_。更正它。
  • 我仍然认为reinterpret_cast 的取消引用结果是未定义的行为。为什么是可变大小的对象?好老的std::vector 会员有什么问题?
  • 除了取消引用之外,您还会对reinterpret_cast 的结果做什么?我不是语言律师,所以它可能是未定义的行为,但我想不出这种转换可能会失败的任何情况。还有关于我不使用std::vector的原因:称之为过早优化:)
  • 您唯一可以对reinterpret_cast 结果做的事情是将其转换回原始类型,前提是中间类型足够大以容纳转换后的值。任何其他用途都是UB。 this + 1 也是 UB,除非在数组内的对象中使用。

标签: c++ alloca


【解决方案1】:

我根本不建议使用 alloca。如果 dim 值很小,那么类中的一些固定大小的缓冲区就足够了。如果 dim 很大,那么与在迭代器上执行的其他操作的复杂性相比,堆分配成本可以忽略不计(请注意,对于非常大的 dim 值,alloca 可能会导致堆栈溢出)。您可以在运行时在固定缓冲区和堆分配之间进行选择,具体取决于昏暗大小。

所以我会推荐类似于 std::string 中的小字符串优化的方法。

也许,某种 COW(写入时复制 http://en.wikipedia.org/wiki/Copy-on-write)技术也可能对您的迭代器有用。

请注意,这种技术不能用于 alloca,只能用于堆分配。此外,如果迭代器使用 alloca(至少没有越来越丑陋的宏),则几乎不可能复制或复制初始化迭代器。

Alloca 是邪恶的 :)

【讨论】:

    【解决方案2】:

    不幸的是,鉴于dim 是一个运行时值,除了使用宏之外别无他法:

    #define CREATE_ITERATOR(dim, sizes) \
        Iterator::init(dim, sizes, alloca(Iterator::alloc_size(dim)))
    

    【讨论】:

      【解决方案3】:

      我的建议可能不是您想要的,但为什么不使用 create|make_iterator 函数来执行 alloca 调用?

      【讨论】:

      • 这不起作用,因为alloca分配的内存会在函数返回后自动释放,如上所述。
      【解决方案4】:

      您可以将维度参数作为模板参数。

      【讨论】:

      • 不,我需要在运行时指定它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-11
      • 2012-10-31
      • 1970-01-01
      • 2011-06-03
      • 2015-10-24
      • 1970-01-01
      • 2021-05-11
      相关资源
      最近更新 更多