用平坦的记忆结构替换矢量矢量

时间:2016-12-11 00:21:02

标签: c++ c++11 vector stl

我有以下类型:

std::vector<std::vector<int>> indicies

其中内部向量的大小始终为2.问题是,向量在内存中是非连续的。我想用连续的东西替换内部向量,以便我可以投射扁平数组:

int *array_a = (int *) &(a[0][0])

如果新类型具有[]运算符,那将是很好的,因此我不必更改整个代码。 (如果有必要,我也可以自己实施)。我的想法是:

std::vector<std::array<int, 2>>

std::vector<std::pair<int, int>>

这些在内存中看起来如何?我写了一个小测试:

#include <iostream>
#include <array>
#include <vector>
int main(int argc, char *argv[])
{
    using namespace std;

    vector<array<int, 2>> a(100);

    cout << sizeof(array<int, 2>) << endl;

    for(auto i = 0; i < 10; i++){
        for(auto j = 0; j < 2; j++){
            cout << "a[" << i << "][" << j << "] " 
                <<&(a[i][j]) << endl;
        }
    }
    return 0;
}

导致:

8
a[0][0] 0x1b72c20
a[0][1] 0x1b72c24
a[1][0] 0x1b72c28
a[1][1] 0x1b72c2c
a[2][0] 0x1b72c30
a[2][1] 0x1b72c34
a[3][0] 0x1b72c38
a[3][1] 0x1b72c3c
a[4][0] 0x1b72c40
a[4][1] 0x1b72c44
a[5][0] 0x1b72c48
a[5][1] 0x1b72c4c
a[6][0] 0x1b72c50
a[6][1] 0x1b72c54
a[7][0] 0x1b72c58
a[7][1] 0x1b72c5c
a[8][0] 0x1b72c60
a[8][1] 0x1b72c64
a[9][0] 0x1b72c68
a[9][1] 0x1b72c6c

在这种情况下似乎有效。这种行为是标准还是巧合?有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

array<int,2>将是一个包含数组int[2]的结构;标准没有直接强制要求,但实际上没有其他理智和实用的方法。

参见标准中的23.3.7 [array]。我可以找到的标准中没有任何内容需要sizeof(std::array<char, 10>)==1024为假。这将是一个荒谬的QOI(实施质量);我见过的每一个实现都有sizeof(std::array<T,N>) == N*sizeof(T),以及我认为是敌对的任何其他内容。

数组必须是连续的容器,这些容器是可以由最多N个可转换为T的类型的std::array<int,2>参数初始化的聚合。

标准允许在这样的阵列之后填充。我知道有0个编译器插入这样的填充。

不能保证连续int的缓冲区可以作为int[3][7]的平缓冲区安全地访问。事实上,别名规则几乎肯定会禁止这种访问作为未定义的行为。您甚至无法使用int*执行此操作! See this SO question and answerand herehere

大多数编译器会使您描述的内容有效,但优化器可能会决定通过array<int,2>*[]访问无法访问相同的内存,并生成疯狂的结果。这似乎不值得。

符合标准的方法是编写一个数组视图类型(它需要两个指针并形成一个可重复的[]重载的可迭代范围)。然后编写一个平面缓冲区的2d视图,其中较低的维度是运行时或编译时间值。然后它的boost将返回一个数组视图。

auto inner=outer[i]和其他&#34;标准扩展程序&#34;将会有代码。图书馆为你做这件事。

将2d视图与拥有矢量的类型合并,然后得到2d矢量。

唯一的行为区别在于,当矢量代码的旧向量复制较低维度(如Set Test1 = Profile Test1.FirstName = "MyFN" MsgBox Test1.FirstName Set Test2 = Profile Test2.Lastname = "MyLast" MsgBox Test2.Lastname MsgBox Test2.FristName Public Function Profile() If IsEmpty(Profile) Then Set Profile = New objectProfile End If End Function Class objectProfile Private mFirstname, mLastname Private Sub Class_Initialize() mFirstname = "" End Sub Public Property Let FirstName(strFirstName) mFirstname = strFirstName End Property Public Property Get FirstName() FirstName = mFirstname End Property Public Property Let Lastname(strLastname) mLastname = strLastname End Property Public Property Get Lastname() Lastname = mLastname End Property End Class )时,它会复制数据,而不是创建视图。

答案 1 :(得分:2)

  

有更好的方法吗?

我最近完成了另一个版本的Game-of-Life。

游戏板是2d,是的,矢量矢量浪费了空间。

在我最近的努力中,我选择为2D游戏板尝试1d矢量。

typedef std::vector<Cell_t*>  GameBoard_t;

然后我创建了一个简单的索引函数,当使用row / col添加到代码的可读性时:

inline size_t gbIndx(int row, int col)
  { return ((row * MAXCOL) + col); }

示例:访问第27行,第33行:

Cell_t* cell = gameBoard[ gbIndx(27, 33) ];

gameBoard中的所有Cell_t *现在都是背靠背打包(矢量的定义),并且使用gbIndx()以行/列顺序访问(初始化,显示等)是微不足道的。

此外,我可以使用简单索引进行各种努力:

void setAliveRandom(const GameBoard_t& gameBoard)
{
   GameBoard_t  myVec(m_gameBoard); // copy cell vector

   time_t seed = std::chrono::system_clock::
        now().time_since_epoch().count();

   // randomize copy's element order
   std::shuffle (myVec.begin(), myVec.end(), std::default_random_engine(seed));

   int count = 0;
   for ( auto it : myVec )
   {  
      if (count & 1)  it->setAlive(); // touch odd elements
      count += 1;
   }
}

我很惊讶我不经常需要行/列索引。

答案 2 :(得分:-4)

据我所知,std::vector在记忆中是连续的。看看这个问题:

Why is std::vector contiguous?

Are std::vector elements guaranteed to be contiguous?

如果你必须调整内部向量的大小,你不会让整个结构连续,但内部向量仍然是它。但是,如果你使用向量向量,你就会有一个完全连续的结构(我在这里编辑,抱歉我误解了你的问题)意味着指向你内部向量的指针也将是连续的。

如果要实现一个始终连续的结构,从第一个向量的第一个元素到最后一个向量的最后一个元素,可以将其实现为具有vector<int>和{的自定义类。 {1}}表示每个内部向量中的元素数。

然后,您可以重载elems_per_vector,以便访问您实际访问的operator() a(i,j)。但是,要插入新元素,并且为了使内部矢量在它们之间保持恒定大小,您必须制作与内部矢量一样多的插入。