如何正确清理对象指针向量中的元素
How to properly clean up elements from vectors of object pointers
我一直在研究动态分配,在 Whosebug 上遇到了这个问题:
Deallocating objects stored in a vector?
一个赞成的答案解释了如何在使用 "Vectors of Pointers to Objects" 时手动管理内存:遍历调用 delete 的向量。
我的问题是关于如何删除向量的特定元素,而不是整个向量。在下面的示例程序中,我有一个对象指针向量。想象一下,这些对象的 x 变量随着时间的推移而递减......当一个对象的 x 值达到一个数字(比方说 3)时,我希望删除该对象;但是,我想始终按对象的 x 值保持向量可排序。
问题是,当我对 x 值达到 3 的对象调用 delete 时,该对象被删除,但那里仍然有一个指向随机内存位置的指针,并且向量的大小也保持不变。
当我循环打印 x 值的向量时,我调用 delete 的元素仍然存在,但指向类似 -53408995 的值。如何摆脱向量的指针元素以及对象?
调用 erase 不是一个选项,因为在我的实际程序中(不是下面的最小示例),矢量会根据其他因素连续排序,这些因素会改变 x 值等价物。我无法跟踪他们的索引。当我遍历向量以检查 x 值时,我想同时删除对象和指针元素。
示例:
#include <iostream>
#include <vector>
class A
{
public:
A(int i) { x = i; }
int x;
};
int main()
{
std::vector<A*> Vec;
Vec.push_back(new A{ 5 });
Vec.push_back(new A{ 4 });
Vec.push_back(new A{ 3 });
std::cout << "Size before = " << Vec.size() << std::endl; // 3
for (auto& a : Vec)
{
std::cout << a->x << std::endl;
if (a->x == 3) { delete a; }
}
std::cout << "Size after = " << Vec.size() << std::endl; // Still 3!
for (auto& a : Vec)
{
std::cout << a->x << std::endl; // Prints 5, 4 and a random memory location like -34528374
}
return 0;
}
在这种情况下,您必须使用 std::list 容器
#include <iostream>
#include <list>
class A
{
public:
A(int i) { x = i; }
int x;
};
int main()
{
std::list<A*> Vec;
Vec.push_back(new A{ 5 });
Vec.push_back(new A{ 4 });
Vec.push_back(new A{ 3 });
std::cout << "Size before = " << Vec.size() << std::endl;
for (auto& a : Vec)
{
std::cout << a->x << std::endl;
}
Vec.remove_if([](A* a){
bool status = (a->x == 3);
if(status){
delete a;
return true;
}else{
return false;
}
});
std::cout << "Size after = " << Vec.size() << std::endl;
for (auto& a : Vec)
{
std::cout << a->x << std::endl;
}
return 0;
}
输出:
Size before = 3
5
4
3
Size after = 2
5
4
我重写了您的代码并添加了一些改进
#include <iostream>
#include <list>
class A
{
public:
A(const int& i):x(i) {} // so X is assigned to i in construction ( more efficient )
int get_x() const {return x;}
private:
int x; // x have to be private ( good practice )
};
int main()
{
std::list<A> List; // A instead of A* so the process of construction / destruction is handled automatically
List.emplace_back(5); // append element and constructed at the same time
List.emplace_back(4); // see std::list for more details
List.emplace_back(3);
std::cout << "Size before = " << List.size() << std::endl;
for (auto& a : List)
{
std::cout << a.get_x() << std::endl;
}
List.remove_if([](A a){ return a.get_x() == 3;});
std::cout << "Size after = " << List.size() << std::endl;
for (auto& a : List)
{
std::cout << a.get_x() << std::endl;
}
return 0;
}
std::vector<std::unique_ptr<A>> vec;
这将处理正常的删除并通过异常退出。
您在评论中提到对象将有其他指向它们的指针。这听起来像 std::shared_ptr<A>
对我来说。这样你就可以有其他指针指向你的 A
对象而不会出现内存泄漏问题。 (std::shared_ptr
会带来小的(!)性能成本,但您现在不必担心)。
此外,我将您的段落更改为 delete/erase 您的矢量元素。请注意,如果有其他实例保留 std::shared_ptr<A>
副本,A
对象仍然存在(但这是一件好事)。
代码如下:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
class A
{
public:
A(int i) { x = i; }
int x;
};
int main()
{
std::vector<std::shared_ptr<A>> Vec;
Vec.emplace_back(std::make_shared<A>(5));
Vec.emplace_back(std::make_shared<A>(4));
Vec.emplace_back(std::make_shared<A>(3));
std::cout << "Size before = " << Vec.size() << std::endl;
Vec.erase(
std::remove_if(std::begin(Vec),std::end(Vec), [](auto&& ptr){ return ptr->x == 3;}),
std::end(Vec));
std::cout << "Size after = " << Vec.size() << std::endl;
for (auto&& a : Vec)
{
std::cout << a->x << std::endl;
}
return 0;
}
在稍微研究了迭代器之后,我也想出了一个使用原始指针的答案:
for (std::vector<A*>::iterator it = Vec1.begin(); it != Vec1.end(); )
{
if ((*it)->x == 3)
{
delete * it;
it = Vec1.erase(it);
}
else
{
++it;
}
}
我将保留 phön 的 post 作为答案,因为如果可用的话,智能指针应始终优先于原始指针。
我一直在研究动态分配,在 Whosebug 上遇到了这个问题:
Deallocating objects stored in a vector?
一个赞成的答案解释了如何在使用 "Vectors of Pointers to Objects" 时手动管理内存:遍历调用 delete 的向量。
我的问题是关于如何删除向量的特定元素,而不是整个向量。在下面的示例程序中,我有一个对象指针向量。想象一下,这些对象的 x 变量随着时间的推移而递减......当一个对象的 x 值达到一个数字(比方说 3)时,我希望删除该对象;但是,我想始终按对象的 x 值保持向量可排序。
问题是,当我对 x 值达到 3 的对象调用 delete 时,该对象被删除,但那里仍然有一个指向随机内存位置的指针,并且向量的大小也保持不变。
当我循环打印 x 值的向量时,我调用 delete 的元素仍然存在,但指向类似 -53408995 的值。如何摆脱向量的指针元素以及对象?
调用 erase 不是一个选项,因为在我的实际程序中(不是下面的最小示例),矢量会根据其他因素连续排序,这些因素会改变 x 值等价物。我无法跟踪他们的索引。当我遍历向量以检查 x 值时,我想同时删除对象和指针元素。
示例:
#include <iostream>
#include <vector>
class A
{
public:
A(int i) { x = i; }
int x;
};
int main()
{
std::vector<A*> Vec;
Vec.push_back(new A{ 5 });
Vec.push_back(new A{ 4 });
Vec.push_back(new A{ 3 });
std::cout << "Size before = " << Vec.size() << std::endl; // 3
for (auto& a : Vec)
{
std::cout << a->x << std::endl;
if (a->x == 3) { delete a; }
}
std::cout << "Size after = " << Vec.size() << std::endl; // Still 3!
for (auto& a : Vec)
{
std::cout << a->x << std::endl; // Prints 5, 4 and a random memory location like -34528374
}
return 0;
}
在这种情况下,您必须使用 std::list 容器
#include <iostream>
#include <list>
class A
{
public:
A(int i) { x = i; }
int x;
};
int main()
{
std::list<A*> Vec;
Vec.push_back(new A{ 5 });
Vec.push_back(new A{ 4 });
Vec.push_back(new A{ 3 });
std::cout << "Size before = " << Vec.size() << std::endl;
for (auto& a : Vec)
{
std::cout << a->x << std::endl;
}
Vec.remove_if([](A* a){
bool status = (a->x == 3);
if(status){
delete a;
return true;
}else{
return false;
}
});
std::cout << "Size after = " << Vec.size() << std::endl;
for (auto& a : Vec)
{
std::cout << a->x << std::endl;
}
return 0;
}
输出:
Size before = 3
5
4
3
Size after = 2
5
4
我重写了您的代码并添加了一些改进
#include <iostream>
#include <list>
class A
{
public:
A(const int& i):x(i) {} // so X is assigned to i in construction ( more efficient )
int get_x() const {return x;}
private:
int x; // x have to be private ( good practice )
};
int main()
{
std::list<A> List; // A instead of A* so the process of construction / destruction is handled automatically
List.emplace_back(5); // append element and constructed at the same time
List.emplace_back(4); // see std::list for more details
List.emplace_back(3);
std::cout << "Size before = " << List.size() << std::endl;
for (auto& a : List)
{
std::cout << a.get_x() << std::endl;
}
List.remove_if([](A a){ return a.get_x() == 3;});
std::cout << "Size after = " << List.size() << std::endl;
for (auto& a : List)
{
std::cout << a.get_x() << std::endl;
}
return 0;
}
std::vector<std::unique_ptr<A>> vec;
这将处理正常的删除并通过异常退出。
您在评论中提到对象将有其他指向它们的指针。这听起来像 std::shared_ptr<A>
对我来说。这样你就可以有其他指针指向你的 A
对象而不会出现内存泄漏问题。 (std::shared_ptr
会带来小的(!)性能成本,但您现在不必担心)。
此外,我将您的段落更改为 delete/erase 您的矢量元素。请注意,如果有其他实例保留 std::shared_ptr<A>
副本,A
对象仍然存在(但这是一件好事)。
代码如下:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
class A
{
public:
A(int i) { x = i; }
int x;
};
int main()
{
std::vector<std::shared_ptr<A>> Vec;
Vec.emplace_back(std::make_shared<A>(5));
Vec.emplace_back(std::make_shared<A>(4));
Vec.emplace_back(std::make_shared<A>(3));
std::cout << "Size before = " << Vec.size() << std::endl;
Vec.erase(
std::remove_if(std::begin(Vec),std::end(Vec), [](auto&& ptr){ return ptr->x == 3;}),
std::end(Vec));
std::cout << "Size after = " << Vec.size() << std::endl;
for (auto&& a : Vec)
{
std::cout << a->x << std::endl;
}
return 0;
}
在稍微研究了迭代器之后,我也想出了一个使用原始指针的答案:
for (std::vector<A*>::iterator it = Vec1.begin(); it != Vec1.end(); )
{
if ((*it)->x == 3)
{
delete * it;
it = Vec1.erase(it);
}
else
{
++it;
}
}
我将保留 phön 的 post 作为答案,因为如果可用的话,智能指针应始终优先于原始指针。