关于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

我对以下内容感到好奇:

  1. vClass 的大小是 8 个字节。但是 vClass 类型的 10 元素向量显示 12 字节的大小。为什么是这样 ?我期待 8x10= 80 字节。

  2. 表达式:this - myVect.data()结果输出元素的索引。 我预计我需要 (this - myVect,data())/sizeof(vClass)。 矢量总是这样吗?
    我对数组进行了同样的尝试:vClass myArray[10](this - myArray) 但在这里我得到了一个连续的十六进制数列表(地址?)。 所以 'this' 指针以某种方式链接到向量,而不是数组 ?

如果有人可以解释或确认此行为,我将不胜感激。

我正在 raspberry pi 上使用 g++ 4.9.2 和 c++14 标准编译它。

谢谢

我没有得到这个输出,根据 juanchopanza 的评论,我得到了一个编译器错误。

  1. 一个vector对象由(实现定义的)元数据组成,包括一个指针指向它包含的数据(你从data().你会发现vector的大小是恒定的,不会随着它的size().

  2. 而变化
  3. 您的 this 指针是 vClass * 类型。指针 p 上的指针算法始终适用于 sizeof( p ).

  4. 的倍数

话虽如此,您真的不应该那样戳 class' 的内部结构。 ;-)

  1. 矢量对象本身总是固定大小的。它包含元素数量和指向某些堆存储的指针等信息。 vector的元素存储在堆上,这样元素的个数可以增长(或缩减)。

  2. 两个指针相减得到指针之间的元素数。这不是特定于向量的。您似乎将此与计算数组大小的方法 (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 其中 ab 的类型为 T* 给出ab 之间的 T 个对象的数量,而不是它们的字节距离。


附带说明:std:vector 要求它的模板参数是一个完整的类型——使用已声明但尚未定义的 class 实例化它是错误的,因为你正在做。它可能碰巧(似乎)起作用,但这纯属偶然。

令人惊讶的是,这是在 Visual Studio 2015 年编译的。

  1. 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
  1. 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 ?

这是有效的,因为 thismyVect.data() 都是指向托管数组中 vClass 的指针。您不需要除以 sizeof(vClass),因为指针算术会考虑它们指向的类型的大小。

sizeof(vClass) == sizeof(int),当您递增、递减或减去指针时,它们以与指向类型相同大小的块为单位移动,因此您不必显式递增具有正确大小的指针。您确实得到了索引 0,1,2,... 但换句话说,这也意味着 2 个 vClass'es 将适合此地址和此地址 .

当您使用向量数组尝试此操作时,每个向量在内存中都是连续的(每个句柄)。而且,每个向量都指向堆中自己的数组,因此每个数组完全不相关。但我不知道你用什么代码测试的,所以我不会对此发表评论。

鉴于您的 vClass 实例属于 vectorthis 指针实际上链接到 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",但是您的实例必须确切地知道它们在哪个容器中,并且必须使用实现细节来找到它们的索引(特别是对于向量),并且包含的​​对象不应该需要这样做。

正如我所说,有很多解决方案。这取决于你想做什么。