c++ std::vector<NotAPointer> 如何存储不同大小的对象,以及当它不包含指针时 ++it 如何知道跳转到哪里

nah*_*dev 1 c++ pointers iterator

编辑:TLDR;我是对象切片的受害者,但我对此一无所知。现在原来的问题如下。

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Sal*_*age 6

当按值将 s 存储BaseShape在向量中时,您将体验到所谓的对象切片

基本上,仅派生类包含的所有信息都会被忘记,而实际上仅存储基类的信息。所有对象的行为都将与对象一样BaseClass,唯一的例外是由于切片而破坏了潜在的类不变量。

  • 也尝试使用“noexcept”移动构造函数。`vector` 中的一些代码使用了 `move_if_noexcept` 方法。不过我已经忘记了具体的细节。 (2认同)