关于c++中vector的观察
Observation about vector in c++
我正在从 C 转向 C++,并且正在玩向量。
这是我试过的一些代码:
#include <iostream>
#include <vector>
using namespace std ;
#define VSIZE 10
class vClass ;
vector <vClass> myVect(VSIZE) ;
class vClass
{
int pos1 ;
public:
void getInfo()
{
pos1 = this - myVect.data() ;
cout << pos1 << endl ;
}
};
//===============================
vClass vArray[VSIZE] ;
int main()
{
cout << endl << "Size of vClass: " << sizeof(vClass) << endl << endl ;
cout << "Size of myVect: " << sizeof(myVect) << endl << endl ;
cout << "Size of myVect[0]: " << sizeof myVect[0] << endl << endl ;
cout << endl << "Locations:" << endl ;
for(int i= 0; i < VSIZE; i++) myVect[i].getInfo();
return 0 ;
}
我得到以下输出:
Size of vClass: 8
Size of myVect: 12
Size of myVect[0]: 8
Locations:
0
1
2
3
4
5
6
7
8
9
我对以下内容感到好奇:
vClass 的大小是 8 个字节。但是 vClass 类型的 10 元素向量显示 12 字节的大小。为什么是这样 ?我期待 8x10= 80 字节。
表达式:this - myVect.data()
结果输出元素的索引。
我预计我需要 (this - myVect,data())/sizeof(vClass)
。
矢量总是这样吗?
我对数组进行了同样的尝试:vClass myArray[10]
和 (this - myArray)
但在这里我得到了一个连续的十六进制数列表(地址?)。
所以 'this' 指针以某种方式链接到向量,而不是数组 ?
如果有人可以解释或确认此行为,我将不胜感激。
我正在 raspberry pi 上使用 g++ 4.9.2 和 c++14 标准编译它。
谢谢
我没有得到这个输出,根据 juanchopanza 的评论,我得到了一个编译器错误。
一个vector
对象由(实现定义的)元数据组成,包括一个指针指向它包含的数据(你从data()
.你会发现vector的大小是恒定的,不会随着它的size()
.
而变化
您的 this
指针是 vClass *
类型。指针 p
上的指针算法始终适用于 sizeof( p )
.
的倍数
话虽如此,您真的不应该那样戳 class' 的内部结构。 ;-)
矢量对象本身总是固定大小的。它包含元素数量和指向某些堆存储的指针等信息。 vector的元素存储在堆上,这样元素的个数可以增长(或缩减)。
两个指针相减得到指针之间的元素数。这不是特定于向量的。您似乎将此与计算数组大小的方法 (sizeof(vArray)/sizeof(vClass)
) 混淆了。向量不需要这个,因为它有一个 size()
成员。
this
指针仅存在于 classes 的成员函数中。它告诉您该函数是针对可能很多 class 个对象的哪个实例调用的。当您执行 myVect[i].getInfo()
时,this
将在 getInfo
函数内部保存 myVect[i]
的地址。
sizeof(myVect)
告诉您矢量对象本身的大小(以字节为单位)(内部很可能是 3 个指针)。它没有说明向量中存储的元素数量;这些由向量管理,但存储在动态分配的内存中,因此它们不会影响 sizeof(myVect)
.
您要查找的信息可能是 myVect.size()
,它为您提供向量中元素的 数量 ,或 myVect.size() * sizeof(vClass)
,它将提供这些向量元素占用的字节数。请注意,后者实际上没有说明向量使用的内存,它可以过度分配以保留 space 用于添加元素等
this - myVect.data()
很可能是未定义行为,因为您不能保证使用指向同一数组的指针。但如果定义明确,它将像任何其他指针算法一样工作(与 C 中相同): a - b
其中 a
和 b
的类型为 T*
给出a
和 b
之间的 T
个对象的数量,而不是它们的字节距离。
附带说明:std:vector
要求它的模板参数是一个完整的类型——使用已声明但尚未定义的 class 实例化它是错误的,因为你正在做。它可能碰巧(似乎)起作用,但这纯属偶然。
令人惊讶的是,这是在 Visual Studio 2015 年编译的。
- Size of vClass is 8 bytes. But a 10 element vector of type vClass shows size of 12 bytes. Why is this ? I was expecting 8x10= 80 bytes.
嗯,vector
只是一个句柄。它的元素存储在堆中。 vector
需要的只是堆中这些元素的指针(data
),当前元素个数(size
)和最大元素个数(capacity
) .
所以如果你 grow/shrink 向量,句柄的大小总是保持不变,只有堆中的数组会改变。
vector< vClass > myVect;
// Stack - vector
[ vector: data, size, capacity ]
|
|
V
// Heap - array managed by vector
[ vClass0 ][ vClass1 ]...
// Each has completely unrelated addresses
- The expression: this - myVect.data() results in outputting the index of the elements. I expected I would need (this -
myVect,data())/sizeof(vClass). Is this always the case with vectors ?
I tried the same with an array : vClass myArray[10] and (this -
myArray) but here i got a list of sequential hexadecimal numbers
(addresses?). So the 'this' pointer is somehow linked to the vector,
but not the array ?
这是有效的,因为 this
和 myVect.data()
都是指向托管数组中 vClass
的指针。您不需要除以 sizeof(vClass)
,因为指针算术会考虑它们指向的类型的大小。
sizeof(vClass) == sizeof(int)
,当您递增、递减或减去指针时,它们以与指向类型相同大小的块为单位移动,因此您不必显式递增具有正确大小的指针。您确实得到了索引 0,1,2,... 但换句话说,这也意味着 2 个 vClass'es 将适合此地址和此地址 .
当您使用向量数组尝试此操作时,每个向量在内存中都是连续的(每个句柄)。而且,每个向量都指向堆中自己的数组,因此每个数组完全不相关。但我不知道你用什么代码测试的,所以我不会对此发表评论。
鉴于您的 vClass
实例属于 vector
,this
指针实际上链接到 vector
的内部数组而不是 vector
自行处理。
之所以有效,是因为您的 class 是 POD-class。如果你要创建一个指针向量(每个实例都在不相关的地址中),或者使用一个指向基数 class 的指针(每个实例在一个不相关的地址和派生类型中都会有不同的大小),那么这会没用。
虽然这个例子有效,但我不鼓励你这样做(此外这只有效,因为你的向量是一个全局变量)。您正在从 C 迁移到 C++,我知道旧习惯很难改掉,但除非您确实必须使用指针算法,否则请避免使用它。
您可以通过(您做对了)调用 size()
方法来获取 vector
的大小,如果您需要知道元素的索引,您将在迭代向量时知道. (当然还有其他方法,但这取决于你)
编辑:关于索引和迭代
当您迭代数组或向量时,您可以按索引进行迭代(以及其他形式的迭代)。很明显,每次迭代你都知道一个索引,
for ( int i = 0; i < myVect.size(); ++i ) {
myVector[ i ]; // <-- here's an object with index i, easy
}
这基本上就是您打印矢量时所做的。
如果你需要在不迭代的情况下知道一个特定的索引(无论你需要它来擦除一个元素,还是直接访问等),你可以,例如,有一个包含相关索引的单独容器,或者将它存储在作为 ID 的对象,例如
class vClass {
// ...
int ID; // Or index, but I think ID sounds better
// ...
};
然后当您将元素推入向量时,您可以设置它们的 ID,它们将知道它们的索引。当然,如果对向量进行排序,则必须自己更新索引。它们不会像您使用指针时那样 "auto-update",但是您的实例必须确切地知道它们在哪个容器中,并且必须使用实现细节来找到它们的索引(特别是对于向量),并且包含的对象不应该需要这样做。
正如我所说,有很多解决方案。这取决于你想做什么。
我正在从 C 转向 C++,并且正在玩向量。
这是我试过的一些代码:
#include <iostream>
#include <vector>
using namespace std ;
#define VSIZE 10
class vClass ;
vector <vClass> myVect(VSIZE) ;
class vClass
{
int pos1 ;
public:
void getInfo()
{
pos1 = this - myVect.data() ;
cout << pos1 << endl ;
}
};
//===============================
vClass vArray[VSIZE] ;
int main()
{
cout << endl << "Size of vClass: " << sizeof(vClass) << endl << endl ;
cout << "Size of myVect: " << sizeof(myVect) << endl << endl ;
cout << "Size of myVect[0]: " << sizeof myVect[0] << endl << endl ;
cout << endl << "Locations:" << endl ;
for(int i= 0; i < VSIZE; i++) myVect[i].getInfo();
return 0 ;
}
我得到以下输出:
Size of vClass: 8
Size of myVect: 12
Size of myVect[0]: 8
Locations:
0
1
2
3
4
5
6
7
8
9
我对以下内容感到好奇:
vClass 的大小是 8 个字节。但是 vClass 类型的 10 元素向量显示 12 字节的大小。为什么是这样 ?我期待 8x10= 80 字节。
表达式:
this - myVect.data()
结果输出元素的索引。 我预计我需要(this - myVect,data())/sizeof(vClass)
。 矢量总是这样吗?
我对数组进行了同样的尝试:vClass myArray[10]
和(this - myArray)
但在这里我得到了一个连续的十六进制数列表(地址?)。 所以 'this' 指针以某种方式链接到向量,而不是数组 ?
如果有人可以解释或确认此行为,我将不胜感激。
我正在 raspberry pi 上使用 g++ 4.9.2 和 c++14 标准编译它。
谢谢
我没有得到这个输出,根据 juanchopanza 的评论,我得到了一个编译器错误。
一个
vector
对象由(实现定义的)元数据组成,包括一个指针指向它包含的数据(你从data()
.你会发现vector的大小是恒定的,不会随着它的size()
. 而变化
您的
this
指针是vClass *
类型。指针p
上的指针算法始终适用于sizeof( p )
. 的倍数
话虽如此,您真的不应该那样戳 class' 的内部结构。 ;-)
矢量对象本身总是固定大小的。它包含元素数量和指向某些堆存储的指针等信息。 vector的元素存储在堆上,这样元素的个数可以增长(或缩减)。
两个指针相减得到指针之间的元素数。这不是特定于向量的。您似乎将此与计算数组大小的方法 (
sizeof(vArray)/sizeof(vClass)
) 混淆了。向量不需要这个,因为它有一个size()
成员。
this
指针仅存在于 classes 的成员函数中。它告诉您该函数是针对可能很多 class 个对象的哪个实例调用的。当您执行 myVect[i].getInfo()
时,this
将在 getInfo
函数内部保存 myVect[i]
的地址。
sizeof(myVect)
告诉您矢量对象本身的大小(以字节为单位)(内部很可能是 3 个指针)。它没有说明向量中存储的元素数量;这些由向量管理,但存储在动态分配的内存中,因此它们不会影响 sizeof(myVect)
.
您要查找的信息可能是 myVect.size()
,它为您提供向量中元素的 数量 ,或 myVect.size() * sizeof(vClass)
,它将提供这些向量元素占用的字节数。请注意,后者实际上没有说明向量使用的内存,它可以过度分配以保留 space 用于添加元素等
this - myVect.data()
很可能是未定义行为,因为您不能保证使用指向同一数组的指针。但如果定义明确,它将像任何其他指针算法一样工作(与 C 中相同): a - b
其中 a
和 b
的类型为 T*
给出a
和 b
之间的 T
个对象的数量,而不是它们的字节距离。
附带说明:std:vector
要求它的模板参数是一个完整的类型——使用已声明但尚未定义的 class 实例化它是错误的,因为你正在做。它可能碰巧(似乎)起作用,但这纯属偶然。
令人惊讶的是,这是在 Visual Studio 2015 年编译的。
- Size of vClass is 8 bytes. But a 10 element vector of type vClass shows size of 12 bytes. Why is this ? I was expecting 8x10= 80 bytes.
嗯,vector
只是一个句柄。它的元素存储在堆中。 vector
需要的只是堆中这些元素的指针(data
),当前元素个数(size
)和最大元素个数(capacity
) .
所以如果你 grow/shrink 向量,句柄的大小总是保持不变,只有堆中的数组会改变。
vector< vClass > myVect;
// Stack - vector
[ vector: data, size, capacity ]
|
|
V
// Heap - array managed by vector
[ vClass0 ][ vClass1 ]...
// Each has completely unrelated addresses
- The expression: this - myVect.data() results in outputting the index of the elements. I expected I would need (this - myVect,data())/sizeof(vClass). Is this always the case with vectors ? I tried the same with an array : vClass myArray[10] and (this - myArray) but here i got a list of sequential hexadecimal numbers (addresses?). So the 'this' pointer is somehow linked to the vector, but not the array ?
这是有效的,因为 this
和 myVect.data()
都是指向托管数组中 vClass
的指针。您不需要除以 sizeof(vClass)
,因为指针算术会考虑它们指向的类型的大小。
sizeof(vClass) == sizeof(int)
,当您递增、递减或减去指针时,它们以与指向类型相同大小的块为单位移动,因此您不必显式递增具有正确大小的指针。您确实得到了索引 0,1,2,... 但换句话说,这也意味着 2 个 vClass'es 将适合此地址和此地址 .
当您使用向量数组尝试此操作时,每个向量在内存中都是连续的(每个句柄)。而且,每个向量都指向堆中自己的数组,因此每个数组完全不相关。但我不知道你用什么代码测试的,所以我不会对此发表评论。
鉴于您的 vClass
实例属于 vector
,this
指针实际上链接到 vector
的内部数组而不是 vector
自行处理。
之所以有效,是因为您的 class 是 POD-class。如果你要创建一个指针向量(每个实例都在不相关的地址中),或者使用一个指向基数 class 的指针(每个实例在一个不相关的地址和派生类型中都会有不同的大小),那么这会没用。
虽然这个例子有效,但我不鼓励你这样做(此外这只有效,因为你的向量是一个全局变量)。您正在从 C 迁移到 C++,我知道旧习惯很难改掉,但除非您确实必须使用指针算法,否则请避免使用它。
您可以通过(您做对了)调用 size()
方法来获取 vector
的大小,如果您需要知道元素的索引,您将在迭代向量时知道. (当然还有其他方法,但这取决于你)
编辑:关于索引和迭代
当您迭代数组或向量时,您可以按索引进行迭代(以及其他形式的迭代)。很明显,每次迭代你都知道一个索引,
for ( int i = 0; i < myVect.size(); ++i ) {
myVector[ i ]; // <-- here's an object with index i, easy
}
这基本上就是您打印矢量时所做的。
如果你需要在不迭代的情况下知道一个特定的索引(无论你需要它来擦除一个元素,还是直接访问等),你可以,例如,有一个包含相关索引的单独容器,或者将它存储在作为 ID 的对象,例如
class vClass {
// ...
int ID; // Or index, but I think ID sounds better
// ...
};
然后当您将元素推入向量时,您可以设置它们的 ID,它们将知道它们的索引。当然,如果对向量进行排序,则必须自己更新索引。它们不会像您使用指针时那样 "auto-update",但是您的实例必须确切地知道它们在哪个容器中,并且必须使用实现细节来找到它们的索引(特别是对于向量),并且包含的对象不应该需要这样做。
正如我所说,有很多解决方案。这取决于你想做什么。