我今天一直在思考一个问题,而且很难在谷歌上找到答案。
我在处理指向堆上和堆栈上分配的对象的指针时,试图理解STL容器的行为。
所以,从对象开始,没有指针......想象我有......
std::vector<int> myVec;
while(true)
{
int myInt = 5;
myVec.push_back(myInt);
myVec.pop_back();
}
我的理解是pop_back()方法将确保删除向量中包含的整数,而不仅仅是从容器中删除。因此,如果这次运行并进行了十亿次迭代,我不应该期望泄漏内存。我插入的所有内容都将被删除。内存检查会显示此行为。
现在考虑我使用指针向量(对堆上的对象)...
std::vector<int*> myVec;
while(true)
{
int * myIntP = new int(5);
myVec.push_back(myIntP);
myVec.pop_back();
}
在这种情况下,每次调用pop_back()时都应该只删除指针本身,并且底层对象保持未删除状态,从而导致内存泄漏。因此,经过十亿次迭代后,我使用了相当多的重要内存,即使我的向量中没有条目。
现在如果我有一个指针向量(对堆栈中的对象)...
std::vector<int*> myVec;
while(true)
{
int myInt = 5;
int * myIntP = &myInt;
myVec.push_back(myIntP);
myVec.pop_back();
}
这里的指针指向堆栈对象。是否在调用pop_back()时释放了内存?内存检查显示我的行为没有内存泄漏。使用少量内存,表明它表现得像堆栈中的对象。然而,这不是我所期待的,因为如果指针已经从另一个函数传递给我,那么就是一个堆栈变量,即
void myFunc(int * myIntP)
{
std::vector<int*> myVec;
myVec.push_back(myIntP);
myVec.pop_back();
}
int main()
{
int myInt = 5;
int * myIntP = &myInt;
myFunc(myIntP);
std::cout << (*myIntP) << std::endl;
return 0;
}
然后允许向量释放此内存,将使myIntP指向已删除的数据。那么这肯定不正确吗?
有人可以帮忙解释一下吗?
还有#34的名称;指向堆栈上变量的指针&#34;即没有用&#34; new&#34;?
初始化由于
乔伊
答案 0 :(得分:3)
while(true)
{
int myInt = 5;
int * myIntP = &myInt;
myVec.push_back(myIntP);
myVec.pop_back();
}
这里实际上只有一个int,myInt
的值为5.循环将重用相同的一个。您将指向该int的指针推入向量,然后将其删除。没有其他事情发生。没有内存泄漏,因为您没有分配新的int。
STD容器指针没有什么不同用于指针,而不是32/64位整数。就他们而言,指针只是另一个数字。因此,如果您将指针插入容器,则您的责任将其删除。
答案 1 :(得分:2)
如果在堆栈上创建指向变量的指针,则变量将在超出范围时被破坏,无论指针如何。并且破坏指针(只要你不对它调用delete)就不会对变量产生影响。
所以,如果你之前停止使用你的指针,没问题,如果你把它存储得更久,问题......
如果你计划在动态分配的变量上使用指针,你应该研究智能指针。
答案 2 :(得分:2)
这里的指针指向堆栈对象。是否在调用pop_back()时释放了内存?
不,他们不是。它们在超出范围时被释放,这发生在}
。在}
之后,内存不再用于此变量(堆栈框架弹出)并将被重用!因此,如果你在推动指针后没有立即弹出指针,那么当变量超出范围时,你的向量将包含一个悬空指针。
答案 3 :(得分:1)
所以,让我们来看看你的每个例子:
std::vector<int> myVec;
while(true)
{
int myInt = 5;
myVec.push_back(myInt);
myVec.pop_back();
}
push_back()方法生成参数的副本并在内部存储副本。因此,如果您存储的是堆栈分配的对象而不是基元,则会调用复制构造函数。 pop_back()方法也不承担任何事情。它会删除您存储的项目的副本(无论是值还是指针),并将其从内部存储中删除。如果存储的副本是堆栈分配的对象,则该类将被分配。当容器管理其内部存储器时,将调用析构函数,因为复制项将不再在范围内。
你的第二个例子:
std::vector<int*> myVec;
while(true)
{
int * myIntP = new int(5);
myVec.push_back(myIntP);
myVec.pop_back();
}
正如您所说,整数在堆上分配。调用push_back()仍然存储参数。在这种情况下,您不存储整数&#34; 5&#34;的值,指针的值,包含&#34的值的内存位置的地址。 5&#34 ;.由于您分配了存储&#34; 5&#34;的内存,因此您负责获取该指针并释放内存。 pop_back()方法不会为您删除指针,也不会返回指针的副本。
你的第三个例子有微妙的差异:
std::vector<int*> myVec;
while(true)
{
int myInt = 5;
int * myIntP = &myInt;
myVec.push_back(myIntP);
myVec.pop_back();
}
在这种情况下,您没有在堆上分配任何内存。您将myInt的地址(堆栈分配值)分配给指针。堆栈内存存在于进程的整个生命周期中,并且不会自行解除分配。但是,一旦离开当前范围(while循环),内存将被其他东西重用。内存仍然存在,但它可能不再具有您期望的值。
你的最后一个例子:
void myFunc(int * myIntP)
{
std::vector<int*> myVec;
myVec.push_back(myIntP);
myVec.pop_back();
}
int main()
{
int myInt = 5;
int * myIntP = &myInt;
myFunc(myIntP);
std::cout << (*myIntP) << std::endl;
return 0;
}
在调用myFunc()之后,你被期望myInt的内存被解除。但是,容器方法不会修改提供的值。他们复制它们。当myFunc()按下myIntP指针时,它正在推送指针,即myIntP指向的地址,而不是该地址的内存中的值。您必须使用call:
取消引用指针myVec.push_back(*myIntP);
请注意,即使您这样做,容器也会复制该值。所以,myInt仍未受影响。
答案 4 :(得分:1)
你混淆并混淆“破坏”和“删除” - 它们是不相同的东西,但在C ++中是两个不同的概念。
删除只能发生指针< - >如果你试图删除非指针,你将得到一个编译时错误。删除首先销毁指向的对象,然后将内存返回到堆中。
另一方面,破坏可能发生在任何事情上,但大多数只对类有兴趣,它会调用析构函数。对于任何没有析构函数的类型(例如int
或任何原始指针类型),destroy都不会执行任何操作。虽然您可以手动销毁对象,但您几乎从不这样做 - 当其他事情发生时,它会自动发生。例如,当局部变量超出范围时,它就会被销毁。
所以在上面的代码中,会发生什么?那么你有一个本地std::vector
当它超出范围时会被销毁。它的析构函数将删除它在内部分配的任何内容,并销毁该向量的所有元素。但是,它不会删除矢量的任何元素。如果你有vector<int>
,那就是全部,因为没有分配任何其他东西,但是当你有vector<int *>
时,如果这些指针被分配,它们就会泄漏。如果他们没有被分配(如果他们指向当地人),则没有任何泄漏。
答案 5 :(得分:0)
我认为你需要深入学习范围变量的有效性 例如:
{
int myVar;//Construct myVar on the stack
}//At this point, myVar is deleted with the stack
在你的最后一个例子中,你在main的开头声明myInt,并且不对myFunc中的值做任何事情。
不丢失myInt数据是正常的。返回0后将被删除;