C++ 如何正确清理对象指针向量中的元素

C++ 如何正确清理对象指针向量中的元素,c++,pointers,vector,memory-management,dynamic-allocation,C++,Pointers,Vector,Memory Management,Dynamic Allocation,我一直在研究动态分配,在StackOverflow上遇到了这个问题: 其中一个向上投票的答案解释了在使用“指向对象的指针向量”时如何手动管理内存:遍历调用delete的向量 我的问题是如何删除向量的特定元素,而不是整个向量。在下面的示例程序中,我有一个对象指针向量。假设这些对象的x变量随时间递减。。。当一个对象的x值达到一个数字(比如3)时,我希望删除该对象;但是,我希望始终按照对象的x值对向量进行排序 问题是,当我对x值达到3的对象调用delete时,该对象被删除,但仍然有一个指向随机内存位

我一直在研究动态分配,在StackOverflow上遇到了这个问题:

其中一个向上投票的答案解释了在使用“指向对象的指针向量”时如何手动管理内存:遍历调用delete的向量

我的问题是如何删除向量的特定元素,而不是整个向量。在下面的示例程序中,我有一个对象指针向量。假设这些对象的x变量随时间递减。。。当一个对象的x值达到一个数字(比如3)时,我希望删除该对象;但是,我希望始终按照对象的x值对向量进行排序

问题是,当我对x值达到3的对象调用delete时,该对象被删除,但仍然有一个指向随机内存位置的指针,并且向量的大小也保持不变

当我在向量中循环打印x值时,我调用delete的元素仍然在那里,但指向像-53408995这样的值。如何除去向量的指针元素和对象

调用erase不是一个选项,因为在我的实际程序中(不是下面的最小示例),向量是按其他因素连续排序的,这些因素会改变x值的等效值。我无法跟踪他们的索引。我想在遍历向量以检查x值时同时删除object和pointer元素

例如:

#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::cout
std::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;
    }
}