我有以下类型:
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
在这种情况下似乎有效。这种行为是标准还是巧合?有更好的方法吗?
答案 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 answer,and here和here。
大多数编译器会使您描述的内容有效,但优化器可能会决定通过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)
。但是,要插入新元素,并且为了使内部矢量在它们之间保持恒定大小,您必须制作与内部矢量一样多的插入。