一个c++std::vector<NotAPointer>如何存储不同大小的对象,++在不包含指针的情况下如何知道跳转到哪里

How can a c++ std::vector<NotAPointer> store objects of different size, and how can ++it know where to jump when it doesn't contain pointers

编辑:TLDR;我是对象切片的受害者,我不知道。现在原来的问题来了。

我试图了解当 MyDerived 的一个实例被 push_backed 时,std::vector<MyClass> 如何存储对象。此外,迭代器如何知道下一个内存块的开始位置,以便增量 ++ 运算符知道如何到达那里。考虑以下代码示例:

#include <iostream>
#include <vector>
using namespace std;

class BaseShape
{
public:
    // BaseShape() { cout << "BaseShape() "; }

    virtual void draw() const { cout << "BASE?\n"; }
};

class Circle : public BaseShape
{
public:
    Circle() { cout << "Circle()"; }

    virtual void draw() const override { cout << "Circle!\n"; }

    void *somePointer, *ptr2;
};

class Triangle : public BaseShape
{
public:
    Triangle() { cout << "Triangle()"; }

    virtual void draw() const override { cout << "Triangle!\n"; }

    void *somePtr, *ptr2, *ptr3, *ptr4, *ptr5;
};

int main()
{

    cout << "vector<BaseShape *> ";
    vector<BaseShape *> pShapes{new BaseShape(), new Circle(), new Triangle(), new Circle()};
    cout << endl;

    for (vector<BaseShape *>::iterator it = pShapes.begin(); it != pShapes.end(); ++it)
    {
        cout << *it << " ";
        (*it)->draw();
    }

    // vector<BaseShape *> Circle()Triangle()Circle()
    // 01162F08 BASE?
    // 01162F18 Circle!
    // 011661A0 Triangle!
    // 01162F30 Circle!

    cout << "\nvector<BaseShape> ";
    vector<BaseShape> shapes{BaseShape(), Circle(), Triangle(), Circle()};
    cout << endl;

    for (vector<BaseShape>::iterator it = shapes.begin(); it != shapes.end(); ++it)
    {
        cout << &(*it) << " ";
        (*it).draw();
    }

    // vector<BaseShape> Circle()Triangle()Circle()
    // 01162FD0 BASE?
    // 01162FD4 BASE?
    // 01162FD8 BASE?
    // 01162FDC BASE?

    return 0;
}

vector::<BaseShape*> pShapes 中,我了解到 pShapes 仅存储指向实际形状地址的指针。然后,很容易知道用 ++it 将内存地址递增多少,因为所有指针都将具有相同的内存大小。控制台输出显示 *it 如何在内存中为“三角形”跳跃。

现在,当使用 vector<BaseShape> shapes 代替时,我的疑问就来了。也许我的理解是错误的,但我相信 shapes 会直接为 BaseShape 对象存储内存(稍后会详细介绍)。但如果这是正确的,那么当我将 push_back 一个 Circle 或一个 Triangle 对象放入其中时,怎么可能将所有对象连续存储在内存中?这听起来不太可能,因为 CircleTriangle 在内存中的大小不同,并且它们的内存必须与 BaseShape 对象的内存连续(例如 [BaseShape mem][Circle mem])。更甚者,++it如何准确知道需要多少内存才能跳转才能获得下一个对象?在控制台输出中,我可以看到 ++it 仅将内存地址增加了 4,这使我得出结论,不知为何只有 BaseShape 部分存储在内存中。 [Circle mem] 是刚刚掉线的吗?因为我可以看到调用了 Circle 构造函数(如 // vector<BaseShape> Circle()Triangle()Circle() 中所示)。

我可能希望代码无法编译或警告我在 shapes 中存储 Circle 或 Triangle 会导致信息丢失,但事实并非如此,并且代码 有点工作。 'kinda' 是因为 draw() 早期绑定到 BaseShape,而不是像虚拟方法那样正确地后期绑定到 Circle 或 Triangle。这表示 shapes 正在存储连续的 BaseShape 内存块...

我不是要在这里解决问题,我只是想知道 C++ 是如何工作的,以及我对 std::vector、指针或迭代器的误解在哪里。

当按值将 BaseShape 存储在向量中时,您将体验到所谓的 object slicing

基本上只有派生的classes包含的所有信息都被遗忘了,只有基础class的信息才真正被存储。所有对象的行为都将与 BaseClass 对象一样,唯一的例外是潜在的 class 不变量由于切片而被破坏。