下面,我不将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;
}
它们都产生相同的结果。有什么区别?
答案 0 :(得分:13)
在内存管理方面,这两个类几乎完全相同。其他几个响应者已经建议两者之间存在差异,因为一个是在堆栈上分配存储而另一个在堆上,但这不一定是真的,即使在它是真的情况下,它也是非常误导的。实际上,所有不同的是分配了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
的实际 meat - 它的内容 - 是从堆中分配的,您无法更改它。
其他人已经指出你的第二个例子中有内存泄漏,这也是事实。确保删除容器类的析构函数中的向量。
答案 1 :(得分:4)
在FieldStorage析构函数中,你必须释放(以防止内存泄漏)第二种情况下为vector分配的内存。
FieldStorage::~FieldStorage()
{
delete my_ints;
}
答案 2 :(得分:3)
正如Mykola Golubyev指出的,你需要在第二种情况下删除矢量。
第一个可能会构建更快的代码,因为优化器知道包含向量的FieldStorage的完整大小,并且可以在一个分配中为两者分配足够的内存。
您的第二个实现需要两个单独的分配来构造对象。
答案 3 :(得分:3)
我认为你真的在寻找Stack and the Heap。
之间的区别第一个在堆栈上分配,而第二个在堆上分配。
答案 4 :(得分:2)
在第一个示例中,对象在堆栈上分配。
第二个例子,对象在堆中分配,并且指向该内存的指针存储在堆栈中。
答案 5 :(得分:1)
第一种方式是使用优先级,你不需要向量上的指针而忘记删除它。
答案 6 :(得分:1)
不同之处在于第二个动态分配向量。差异很小:
你必须释放向量对象占用的内存(向量对象本身,而不是向量中保留的对象,因为它由向量正确处理)。你应该使用一些智能指针来保持向量或make(例如在析构函数中):
删除my_ints;
第一个可能更有效,因为对象是在堆栈上分配的。
访问vector的方法有不同的语法:)
答案 7 :(得分:1)
FieldStorage的第一个版本包含一个向量。 FieldStorage类的大小包括足以容纳向量的字节。构造FieldStorage时,向量是在FieldStorage构造函数的主体执行之前构造的。当FieldStorage被破坏时,向量也是如此。
这不一定在堆栈上分配向量;如果你堆分配一个FieldStorage对象,那么向量的空间来自那个堆分配,而不是堆栈。如果定义了一个全局FieldStorage对象,那么向量的空间既不来自堆栈也不来自堆,而是来自为全局对象指定的空间(例如某些平台上的.data
或.bss
部分)
请注意,向量执行堆分配以保存实际数据,因此它可能只包含几个指针,或指针和几个长度,但它可能包含编译器的STL实现所需的任何内容。
FieldStorage的第二个版本包含一个指向矢量的指针。 FieldStorage类的大小包括指向矢量的指针的空间,而不是实际的矢量。您正在使用FieldStorage构造函数体中的new为向量分配存储,并且在FieldStorage被销毁时泄漏该存储,因为您没有定义删除向量的析构函数。
答案 8 :(得分:0)
在第一种情况下,std :: vector被直接放入你的类中(它正在处理任何内存分配和释放,它需要增长和缩小向量并在你的对象被销毁时释放内存;它是从您那里抽象出内存管理,这样您就不必处理它了。在第二种情况下,你明确地在堆中为std :: vector分配存储空间,并且忘记删除它,所以你的程序会泄漏内存。
答案 9 :(得分:0)
对象的大小将会有所不同。在第二种情况下,Vector&lt;&gt; *仅占用指针的大小(在32位机器上为4字节)。在第一种情况下,您的对象会更大。
答案 10 :(得分:0)
一个实际的区别在于,在第二个示例中,您永远不会释放向量的内存或其内容(因为您不删除它,因此调用它的析构函数)。
但是第一个示例会在销毁FieldStorage对象时自动销毁向量(并释放其内容)。