[英]What's the difference between these two classes?
下面,我不是将 my_ints
声明为指针。 我不知道将分配内存的位置。 请教我这里!
#include <iostream>
#include <vector>
class FieldStorage
{
private:
std::vector<int> my_ints;
public:
FieldStorage()
{
my_ints.push_back(1);
my_ints.push_back(2);
}
void displayAll()
{
for (int i = 0; i < my_ints.size(); i++)
{
std::cout << my_ints[i] << std::endl;
}
}
};
在这里,我将字段my_ints
声明为指针:
#include <iostream>
#include <vector>
class FieldStorage
{
private:
std::vector<int> *my_ints;
public:
FieldStorage()
{
my_ints = new std::vector<int>();
my_ints->push_back(1);
my_ints->push_back(2);
}
void displayAll()
{
for (int i = 0; i < my_ints->size(); i++)
{
std::cout << (*my_ints)[i] << std::endl;
}
}
~FieldStorage()
{
delete my_ints;
}
};
要测试的main()
函数:
int main()
{
FieldStorage obj;
obj.displayAll();
return 0;
}
它们都产生相同的结果。 有什么不同?
在内存管理方面,这两个类几乎完全相同。 其他几个响应者已经建议两者之间存在差异,因为一个是在堆栈上分配存储而另一个在堆上,但这不一定是真的,即使在它是真的情况下,它也是非常误导的。 实际上,所有不同的是分配vector
的元数据的地方; 无论如何, vector
的实际底层存储都是从堆中分配的。
看到这个有点棘手,因为你正在使用std::vector
,所以隐藏了具体的实现细节。 但基本上, std::vector
是这样实现的:
template <class T>
class vector {
public:
vector() : mCapacity(0), mSize(0), mData(0) { }
~vector() { if (mData) delete[] mData; }
...
protected:
int mCapacity;
int mSize;
T *mData;
};
如您所见, vector
类本身只有几个成员 - 容量,大小和指向动态分配的内存块的指针,该内存块将存储向量的实际内容。
在您的示例中,唯一的区别是这些少数字段的存储来自何处。 在第一个示例中,存储是从您用于包含类的任何存储中分配的 - 如果它是堆分配的,那么vector
那些位也是如此。 如果您的容器是堆栈分配的,那么vector
那些位也是如此。
在第二个例子中, vector
那些位总是堆分配的。
在这两个例子中,实际的肉 vector
-它的内容-从堆中分配的,你不能改变的。
其他人已经指出你的第二个例子中有内存泄漏,这也是事实。 确保删除容器类的析构函数中的向量。
在FieldStorage析构函数中,您必须在第二种情况下释放(以防止内存泄漏)为vector分配的内存。
FieldStorage::~FieldStorage()
{
delete my_ints;
}
正如Mykola Golubyev指出的那样,你需要删除第二种情况下的向量。
第一个可能会构建更快的代码,因为优化器知道包括向量的FieldStorage的完整大小,并且可以在一个分配中为两者分配足够的内存。
您的第二个实现需要两个单独的分配来构造对象。
我认为你真的在寻找Stack和Heap之间的区别。
第一个在堆栈上分配,而第二个在堆上分配。
在第一个示例中,对象在堆栈上分配。
第二个例子,对象在堆中分配,并且指向该内存的指针存储在堆栈中。
第一种方式是使用的优先方法,你不需要向量上的指针,忘记删除它。
区别在于第二个动态分配向量。 差异很小:
你必须释放向量对象占用的内存(向量对象本身,而不是向量中保留的对象,因为它由向量正确处理)。 你应该使用一些智能指针来保持向量或make(例如在析构函数中):
删除my_ints;
第一个可能更有效,因为对象是在堆栈上分配的。
访问vector的方法有不同的语法:)
FieldStorage的第一个版本包含一个向量。 FieldStorage类的大小包括足以容纳向量的字节。 构造FieldStorage时,向量是在FieldStorage构造函数的主体执行之前构造的。 当FieldStorage被破坏时,向量也是如此。
这不一定在堆栈上分配向量; 如果你堆分配一个FieldStorage对象,那么向量的空间来自那个堆分配,而不是堆栈。 如果定义全局FieldStorage对象,则向量的空间既不来自堆栈也不来自堆,而是来自为全局对象指定的空间(例如某些平台上的.data
或.bss
部分)。
请注意,向量执行堆分配以保存实际数据,因此它可能只包含几个指针,或指针和几个长度,但它可能包含编译器的STL实现所需的任何内容。
FieldStorage的第二个版本包含一个指向矢量的指针。 FieldStorage类的大小包括指向矢量的指针的空间,而不是实际的矢量。 您正在使用FieldStorage构造函数体中的new为向量分配存储,并且在FieldStorage被销毁时泄漏该存储,因为您没有定义删除向量的析构函数。
在第一种情况下,std :: vector被直接放入你的类中(它正在处理任何内存分配和释放,它需要增长和缩小向量并在对象被销毁时释放内存;它正在抽象内存来自您的管理,以便您不必处理它)。 在第二种情况下,您在某处显式为堆中的std :: vector分配存储空间,并且忘记删除它,因此您的程序将泄漏内存。
对象的大小将会有所不同。 在第二种情况下,Vector <> *仅占用指针的大小(在32位机器上为4字节)。 在第一种情况下,您的对象将更大。
一个实际的区别是,在第二个示例中,您永远不会释放向量的内存或其内容(因为您不删除它,因此调用它的析构函数)。
但是第一个示例将在销毁FieldStorage对象时自动销毁向量(并释放其内容)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.