通过课程访问内存

时间:2011-12-08 00:05:13

标签: c++ memory alignment padding

我有一个特殊的问题。我想创建包含可变长度数组的类。我不想出于局部性的原因在堆上分配数组(当我这样做时,代码减慢了2倍)。我不想使用虚函数,因为我不想为函数调用付费。以下代码可以工作(在我的编译器/平台上),但需要注意。

include <iostream>
include <boost/array.hpp>

struct Base
{
    Base(size_t s):
       size(s)                 {}
    int&        up(size_t i)   { return *(reinterpret_cast<int*>((&size) + 1) + i); }
    size_t      size;
};

template<size_t sz>
struct Derived: public Base
{
    boost::array<int, sz>       data;
    Derived(): Base(sz)         {}
};

int main()
{
    Derived<5> d5;
    d5.data[2] = 1234;
    Base* b = &d5;
    std::cout << b->up(2) << std::endl;
}

这令人难以置信的丑陋; reinterpret_cast&lt; ...&gt;是一面红旗。此外,需要注意的是,如果我将size_t更改为short unsigned(我猜编译器会填充类),则会失败。

所以问题是:有没有办法使这个便携式?有没有办法从Base确定第一个成员变量在其派生类中的位置?

3 个答案:

答案 0 :(得分:3)

我想到了一个想法:让Derived构造函数在Base成员中存储指向其数据的指针。

struct Base
{
protected:
    size_t size;
    int * array;
    Base(size_t s, int * arr):
      size(s), array(arr)
    { }

public:
    int&   up(size_t i)   { return array[i]; }
    size_t getSize() { return size; }
};

template<size_t sz>
struct Derived: public Base
{
    std::array<int, sz> data;

    Derived():
      Base(sz, &data[0])
    { }
};

int main()
{
    Derived<5> d5;
    d5.data[2] = 1234;
    Base* b = &d5;
    std::cout << b->up(2) << std::endl;
}

答案 1 :(得分:2)

从您的评论中,听起来像这样的事情就足够了:

#include <cstddef>
#include <array>
#include <algorithm>

template <typename T>
struct ArrayBase
{
  typedef T type;

  type & operator[](std::size_t i) { return buf[i]; }
  type const & operator[](std::size_t i) const { return buf[i]; }

protected:
  ArrayBase(type * p) : buf(p) { }

private:
  type * buf;
};

template <typename T, std::size_t N>
struct Array : ArrayBase<T>
{
  Array()
  : ArrayBase<T>(a.data())
  {
  }

  Array(Array const & rhs)
  : ArrayBase<T>(a.data())
  {
      std::copy(rhs.a.begin(), rhs.a.end(), a.begin());
  }

private:

  std::array<T, N> a;
};

用法:

Array<int, 5> a5;
ArrayBase<int> & b = a5;
b[2] = 11;
Array<int, 5> a52 = a5;
a52[2] = 13;

答案 2 :(得分:0)

这是一个危险但又快速而小的答案:

template<class T>
struct alloca_magic {
    T* b;
    int s;
    alloca_magic(void* bu, int sz) 
       : b((T*)(bu)),s(sz) 
    {new(b)T[s];}
    ~alloca_magic() 
    {for(int i=0;i<s;++i)(b+i)->~T();}
    operator T*() {return b;}
};
#define alloca_magic(Type,Name,Size) void* t = alloca(sizeof(Type)*Size); alloca_magic<Type> Name(t,Size);

#include <iostream>     
struct test {
    int data;
    test() {std::cout << "ctor\n";}
    ~test() {std::cout << "dtor\n";}
};

void foo(int len) {
   std::cout << "begin foo\n";
   alloca_magic(test ,buffer,len);
    for(int i=0; i<len; i++)
       buffer[i].data = i;
   std::cout << "end foo\n";
}

int main() {
    int len; 
    std::cin >> len;
    std::cout << "begin main\n";
    alloca_magic(test ,buffer,len);
    for(int i=0; i<len; i++)
        buffer[i].data = i;
    foo(len);
    std::cout << "end main\n";
}

http://ideone.com/ccvTR结果:

begin main
ctor
ctor
ctor
begin foo
ctor
ctor
ctor
end foo
dtor
dtor
dtor
end main
dtor
dtor
dtor

对于Windows,您必须将alloca替换为_alloca。请记住,这很容易被滥用和破坏,如果大量完成可能会导致堆栈溢出。除了速度测试之外,我推荐这个,但可能也没有。