C++ 如何正确清理对象指针向量中的元素
我一直在研究动态分配,在StackOverflow上遇到了这个问题: 其中一个向上投票的答案解释了在使用“指向对象的指针向量”时如何手动管理内存:遍历调用delete的向量 我的问题是如何删除向量的特定元素,而不是整个向量。在下面的示例程序中,我有一个对象指针向量。假设这些对象的x变量随时间递减。。。当一个对象的x值达到一个数字(比如3)时,我希望删除该对象;但是,我希望始终按照对象的x值对向量进行排序 问题是,当我对x值达到3的对象调用delete时,该对象被删除,但仍然有一个指向随机内存位置的指针,并且向量的大小也保持不变 当我在向量中循环打印x值时,我调用delete的元素仍然在那里,但指向像-53408995这样的值。如何除去向量的指针元素和对象 调用erase不是一个选项,因为在我的实际程序中(不是下面的最小示例),向量是按其他因素连续排序的,这些因素会改变x值的等效值。我无法跟踪他们的索引。我想在遍历向量以检查x值时同时删除object和pointer元素 例如:C++ 如何正确清理对象指针向量中的元素,c++,pointers,vector,memory-management,dynamic-allocation,C++,Pointers,Vector,Memory Management,Dynamic Allocation,我一直在研究动态分配,在StackOverflow上遇到了这个问题: 其中一个向上投票的答案解释了在使用“指向对象的指针向量”时如何手动管理内存:遍历调用delete的向量 我的问题是如何删除向量的特定元素,而不是整个向量。在下面的示例程序中,我有一个对象指针向量。假设这些对象的x变量随时间递减。。。当一个对象的x值达到一个数字(比如3)时,我希望删除该对象;但是,我希望始终按照对象的x值对向量进行排序 问题是,当我对x值达到3的对象调用delete时,该对象被删除,但仍然有一个指向随机内存位
#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;
}
#包括
#包括
甲级
{
公众:
A(inti){x=i;}
int x;
};
int main()
{
std::Vec;
向量推回(新的A{5});
向量推回(新的A{4});
向量推回(新的A{3});
std::cout在这种情况下,您必须使用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;
}
我重写了你的代码,添加了一些改进
#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;
}
#包括
#包括
甲级
{
公众:
A(const int&i):x(i){}//因此x在构造中被分配给i(更有效)
int get_x()常量{return x;}
私人:
int x;//x必须是私有的(良好实践)
};
int main()
{
std::list list;//A代替了A*,因此构造/销毁过程将自动处理
List.emplace_back(5);//追加元素并同时构造
List.emplace_back(4);//有关详细信息,请参阅std::List
名单.安置(3);;
std::coutstd::vec;
这将处理正常删除并通过异常退出。您在评论中提到,对象将有其他指向它们的指针。对我来说,这听起来像是std::shared_ptr
。这样,您可以有指向A
对象的其他指针,而不会出现内存泄漏问题。(std::shared_ptr
带来了很小的(!)性能成本,但您现在不必担心)。
此外,我还更改了你的文章,从向量中删除/删除了你的元素。请注意,如果有其他实例将std::shared_ptr
复制到它,则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;
}
#包括
#包括
#包括
#包括
甲级
{
公众:
A(inti){x=i;}
int x;
};
int main()
{
std::Vec;
向量放置回(标准::使共享(5));
Vec.emplace_back(标准::使_共享(4));
Vec.emplace_back(标准::使_共享(3));
std::cout在研究了一点迭代器之后,我还提出了一个使用原始指针的答案:
for (std::vector<A*>::iterator it = Vec1.begin(); it != Vec1.end(); )
{
if ((*it)->x == 3)
{
delete * it;
it = Vec1.erase(it);
}
else
{
++it;
}
}
for(std::vector::iterator it=Vec1.begin();it!=Vec1.end();)
{
如果((*it)->x==3)
{
删除*它;
it=Vec1.擦除(it);
}
其他的
{
++它;
}
}
我将保留phön的帖子作为答案,因为智能指针应该总是优先于原始指针(如果可用)。更改:std::Vec;
到std::Vec;
工作完成。或者问问自己,为什么它不仅仅是std::Vec;
好吧,几乎完成了;用std::make替换所有新的_unique
会更好向量还应该包含指向A的派生类的指针。对象还需要在各种其他向量/映射中具有指向它们的指针。在这种情况下,正确的选择是使用std::list@NathanielG.M.remove\u如果从向量中删除unique\u ptr
会破坏它指向的对象。 如果从向量中删除一个shared\u ptr
,如果它是指向该对象的最后一个shared\u ptr
,则该对象将被销毁。这可能会起作用,但在我看来,在这种情况下,这不是正确的方法。为什么您要命名一个std::list
Vec?我只是使用了与问题中的代码相同的名称。我刚刚使用了c将其挂到列表中,感谢您的评论:)我想避免在向量上迭代一段额外的时间来调用erase,但是如果没有其他方法,那么我会接受这个答案,并使用您编写的lambda。谢谢。这不是您需要担心的事情。您不是在向量上简单地迭代2次。如果对对象重新排序,请删除_,以便要删除的所有元素都位于向量的末尾。擦除只需获取此范围,并破坏对象并相应调整向量的大小。这项工作无论如何都必须完成,因此不会对性能造成任何损失
#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;
}
}