一个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
对象放入其中时,怎么可能将所有对象连续存储在内存中?这听起来不太可能,因为 Circle
和 Triangle
在内存中的大小不同,并且它们的内存必须与 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 不变量由于切片而被破坏。
编辑: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
对象放入其中时,怎么可能将所有对象连续存储在内存中?这听起来不太可能,因为 Circle
和 Triangle
在内存中的大小不同,并且它们的内存必须与 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 不变量由于切片而被破坏。