指针的C++容器
C++ Containers of pointers
我今天一直在思考一个问题,很难在谷歌上找到答案。
我试图理解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指向已删除的数据。那么,这肯定不是正确的吗?
有人能帮忙解释一下吗?
还有没有"指向堆栈上变量的指针"的名称,即没有用"new"初始化?
感谢
Joey
while(true)
{
int myInt = 5;
int * myIntP = &myInt;
myVec.push_back(myIntP);
myVec.pop_back();
}
实际上,这里只有一个int,值为5的myInt
。循环将重复使用相同的循环。你把一个指向那个int的指针推到向量中,然后把它移除。没有内存泄漏,因为您没有分配新的int。
STD容器对指针所做的与对32/64位整数所做的没有什么不同。就他们而言,指针只是另一个数字。因此,如果您将指针插入容器,则您有责任删除它。
如果您在堆栈上创建一个指向变量的指针,那么无论指针是什么,当变量超出范围时,它都将被销毁。破坏指针(只要你不调用delete)对变量没有影响。
所以,如果你以前停止使用指针,没问题,如果你存储时间更长,问题。。。
如果您计划在动态分配的变量上使用指针,那么您应该研究智能指针。
这里的指针指向堆栈对象。在调用pop_back()时是否释放了它们的内存?
不,它们不是。当它们超出作用域(发生在}
)时,它们将被释放。}
之后,内存不再用于此变量(堆栈帧弹出),将被重用!因此,如果你没有在按下指针后立即弹出指针,那么当变量超出范围时,你的向量将包含一个悬空指针。
所以,让我们来看看您的每个例子:
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()仍然存储参数。在这种情况下,您没有存储整数"5"的值,指针的值,包含"5"值的内存位置的地址。由于您分配了存储"5"的内存,因此您负责获取该指针并释放内存。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仍然不受影响。
您混淆并混淆了"销毁"answers"删除"——它们不是一回事,但在C++中是两个不同的概念。
只有指针才能执行删除操作——如果您试图删除非指针,则会出现编译时错误。删除首先销毁指向的对象,然后将其内存返回到堆。
另一方面,破坏可以发生在任何事情上,但大多数情况下只对调用析构函数的类感兴趣。对于任何没有析构函数的类型(如int
或任何原始指针类型),销毁都不会起任何作用。虽然你可以手动销毁一个物体,但你几乎从来没有这样做过——当其他事情发生时,它会自动为你发生。例如,当局部变量超出范围时,它将被销毁。
那么在上面的代码中,会发生什么呢?你有一个本地std::vector
,当它超出范围时就会被销毁。它的析构函数将删除它内部分配的任何内容,并销毁向量的所有元素。但是,它不会删除矢量的任何元素。当你有vector<int>
时,这就是全部,因为没有分配其他指针,但当你有一个vector<int *>
时,如果分配了这些指针,它们就会泄漏。如果他们没有被分配(如果他们指向当地人),就没有什么可泄露的。
我认为您需要深入了解范围变量的有效性示例:
{
int myVar;//Construct myVar on the stack
}//At this point, myVar is deleted with the stack
在您的最后一个示例中,您在main的开头声明myInt,并且不对myFunc中的值执行任何操作。
不丢失myInt数据是正常的。它将在返回0之后被擦除;
- 在提升multi_index容器中,是否定义了"default index"?
- 用于访问容器<T>数据成员的正确 API
- Eigen如何在容器循环中干净地附加矩阵
- 模板专用化(按容器):value_type
- 关联容器的下界复杂性:成员函数与非成员函数
- 更多constexpr容器是否需要mark_immutable_if_consexpr
- 在STL容器中使用模板类
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- 检查函数返回类型是否与STL容器类型值相同
- STL算法函数在多个一维容器上的使用
- 如何将带有自定义对象的容器从C++传递到QML
- 在没有未定义行为的情况下实现类似std::vector的容器
- 将 BASE 派生类存储在同一容器中
- 为什么 STL 容器适配器堆栈中的 top 返回常量引用?
- 在C++中迭代 2D 容器的最干净方法
- 将线程中的数据存储到全局容器的最佳方法?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- 使用带有C++对象和标准库容器的插件系统
- 使用 std::index_sequence 初始化具有固定大小数组成员的 POD 结构容器
- 我可以用字符串变量而不是它的名字创建容器吗? C++