C++删除向量中的元素

5
int main(){     

vector<Customer*> newcustomer;

newcustomer.push_back(new Customer("III", 123333, 555));
newcustomer.push_back(new Customer("LOL", 122222, 444));
newcustomer.push_back(new Customer("PPL", 121111, 333));

for (int i = 0; i < 3; i++){
    cout << newcustomer[i]->getName() << endl;
    cout << newcustomer[i]->getPhone() << endl;
    cout << newcustomer[i]->getID() << endl;
    cout << endl;
}






system("pause");
return 0;

}

我创建了一个名为customer的类,你可以插入新的客户,getName返回姓名,getPhone返回电话号码,GetID返回ID。现在,我想要删除vector中的所有内容,但不确定如何做。

5个回答

6

要删除向量中的所有元素,您可以简单地使用myvector.erase(myvector.begin(), myvector.end());myvector.clear()。但问题不仅仅是从向量中擦除元素,还要删除在堆上分配的内存。以下是我的解决方案。

int main(){     

vector<Customer*> newcustomer;

newcustomer.push_back(new Customer("III", 123333, 555));
newcustomer.push_back(new Customer("LOL", 122222, 444));
newcustomer.push_back(new Customer("PPL", 121111, 333));

for (int i = 0; i < 3; i++){
    cout << newcustomer[i]->getName() << endl;
    cout << newcustomer[i]->getPhone() << endl;
    cout << newcustomer[i]->getID() << endl;
    cout << endl;
}


while(!newcustomer.empty())
{
    Customer *cust = newcustomer.front();
    newcustomer.erase(newcustomer.begin());
    delete cust;
}

system("pause");
return 0;

}

1
或者,你可以使用 std::unique_ptr 并忘记所有细节。或者,如果需要,可以使用 std::shared_ptr - Nik Bougalis
是的,那太好了... :-) - HadeS

3

5
这将泄漏为每个对象分配的内存。 - Nik Bougalis
3
如果你真的真的想要擦除所有内容,我们有vector::clear()函数。 - T.C.
1
如果您使用new来创建一个对象,它将一直存在,直到手动删除。所以是的,在清除之前,您必须删除每个对象。幸运的是,有三件事情可以做。第一种方法是不使用指针的向量(vector<Customer> newcustomer),并推送一个临时对象(newcustomer.push_back(Customer("III", 123333, 555));)。临时对象将自动销毁,而向量中的对象将在从向量中移除时销毁。第二种解决方案需要符合C++11标准的编译器。您可以用emplace_back替换推送临时对象,这样可以直接在向量中构造Customer对象。 - user4581301
1
是的,对象的析构函数确实会被调用。那么指针的析构函数是什么呢?提示:它不同于所指向的对象的析构函数。 - Nik Bougalis
1
@Ggbestboi:根据你的需求,你可能需要使用end()而不是begin() + N - user1084944
显示剩余5条评论

1
你可能不应该使用vector<Customer*>;对于大多数应用程序,vector<Customer>会更好,对于其余大多数应用程序,vector<unique_ptr<Customer>>(有时是vector<shared_ptr<Customer>>)会更好。

但是为了回答你的问题,要对向量的每个元素执行操作,你需要使用循环:

for(auto &x: newcustomer) { 
    delete x;
    // x = nullptr; // optional: don't leave a dangling pointer
}

0
在您的函数结束时,newcustomer 将超出范围并调用其析构函数。对于 std::vector 的析构函数会擦除向量的内容,并在适用时调用析构函数。
您可以通过调用 clear 成员函数提前触发此行为:
#include <iostream>
#include <vector>

#include <iostream>
#include <vector>

struct S {
    S(const char* v_) : m_v(v_) {}
    ~S() {
        std::cout << "destructing ~S(" << m_v << ")\n";
    }
    const char* m_v;
};

int main()
{
    std::vector<S> v;
    v.emplace_back("hello");
    v.clear();

    v.emplace_back("fin");
}

http://ideone.com/MyKF6a

你也可以使用迭代器删除特定范围的元素:

v.erase(v.begin(), v.end());

然而:需要注意的是,您正在使用指针,并且从向量中删除指针不会释放它们所指向的实例:

#include <iostream>
#include <vector>

struct S {
    S(const char* v_) : m_v(v_) {}
    ~S() {
        std::cout << "destructing ~S(" << m_v << ")\n";
    }
    const char* m_v;
};

int main()
{
    std::vector<S*> v;
    v.push_back(new S("hello"));
    v.clear();

    v.push_back(new S("fin"));
}

http://ideone.com/QZLRXv - 没有输出,因为没有释放任何内容。

使用new分配的内存必须使用deletedelete[]释放。

您可以自行执行此操作:

for (Customer* ptr : newcustomer) {
    delete ptr;
}
newcustomer.clear();

或者您可以使用其中一个标准指针管理类。

std::vector<std::unique_ptr<Customer>> newcustomer;

newcustomer.emplace_back(std::make_unique<Customer>(...));

完整代码:
#include <iostream>
#include <vector>
#include <memory>

struct Customer
{
    Customer(const char* name_, int i_, int j_)
        : m_name(name_), m_i(i_), m_j(j_) {}
    ~Customer()
    {
        std::cout << "~Customer(" << m_name << ")\n";
    }

    const char* m_name;
    int m_i, m_j;

    const char* getName() const noexcept { return m_name; }
    int getPhone() const noexcept { return m_i; }
    int getID() const noexcept { return m_j; }
};

int main()
{     
    std::vector<std::unique_ptr<Customer>> customers;

    customers.emplace_back(std::make_unique<Customer>("Andy", 123, 111));
    customers.emplace_back(std::make_unique<Customer>("Bob", 124, 222));
    customers.emplace_back(std::make_unique<Customer>("Chris", 125, 333));

    for (auto& ptr : customers) {
        std::cout << ptr->getName() << "\n";
        std::cout << ptr->getPhone() << "\n";
        std::cout << ptr->getID() << "\n";
        std::cout << "\n";
    }

    // remove the first customer
    std::cout << "pop:\n";
    customers.erase(customers.begin());

    // remove the rest
    std::cout << "clear:\n";
    customers.clear();
}

http://ideone.com/3x39LV


0

如上所述,newcustomer.clear()将清空向量,但它不会处理指针。HadeS在他或她的解决方案中解决了这个问题。以下是几种解决问题的方法。

与某些语言不同,在C++中,我们不必使用new创建指针。这种方法丢弃指针并直接将Customer存储在向量中。这通常是最安全的解决方案,因为您必须努力使此解决方案出错。

int main()
{

    vector<Customer> newcustomer;

    newcustomer.push_back(Customer("III", 123333, 555));
    newcustomer.push_back(Customer("LOL", 122222, 444));
    newcustomer.push_back(Customer("PPL", 121111, 333));

    // do stuff

    newcustomer.clear();

    return 0;

}

这有点笨重。你创建了一个临时的Customer(调用构造函数),并将其复制到向量中(调用复制构造函数)。通过在Customer构造函数和析构函数中添加适当的调试语句,你最终得到像这样的垃圾:

create III
copy III
destroy III
create LOL
copy LOL
copy III
destroy III
destroy LOL
create PPL
copy PPL
copy III
copy LOL
destroy III
destroy LOL
destroy PPL
Use III
Use LOL
Use PPL
destroy III
destroy LOL
destroy PPL

创建3个,复制6份,销毁9个。

如果Customer由指针或复杂的自定义数据类型组成,那么很可能需要定义复制构造函数来使其正常工作。参见Rule Of Three

C++11有一种不那么繁琐的方法:

int main()
{

    vector<Customer> newcustomer;

    newcustomer.emplace_back("III", 123333, 555);
    newcustomer.emplace_back("LOL", 122222, 444);
    newcustomer.emplace_back("PPL", 121111, 333);

    // do stuff

    newcustomer.clear();

    return 0;

}

消除了第一个版本中的一些工作。输出看起来像这样:

create III
create LOL
copy III
destroy III
create PPL
copy III
copy LOL
destroy III
destroy LOL
Use III
Use LOL
Use PPL
destroy III
destroy LOL
destroy PPL

3 创建,3 复制,6 销毁。

如果客户端有移动构造函数,则输出如下:

create III
create LOL
move III
destroy 
create PPL
move III
move LOL
destroy 
destroy 
Use III
Use LOL
Use PPL
destroy III
destroy LOL
destroy PPL

3 创建,3 移动,6 销毁。

如果 Customer 复制需要很长时间或使用了大量 RAM,那么实现移动构造函数是值得的,因为您不想临时复制大量 RAM。但是,如果您使用了大量 RAM,通常复制将需要很长时间。

重要提示:看到有一堆没有说明它们是谁的“销毁”吗?这是因为移动构造函数从源移动数据到目标。源不再具有任何数据。移动构造函数不能使源处于这样的状态,即如果在移动后使用源,则会出现未定义行为。欢迎来到五法则

如果出于某种原因必须使用指针,则将它们包装在智能指针中可以使内存管理变得更加容易。例如:

int main()
{

    vector<unique_ptr<Customer>> newcustomer;

    newcustomer.emplace_back(new Customer("III", 123333, 555));
    newcustomer.emplace_back(new Customer("LOL", 122222, 444));
    newcustomer.emplace_back(new Customer("PPL", 121111, 333));

    // do stuff

    newcustomer.clear();

    return 0;

}

这个输出看起来非常棒:

create III
create LOL
create PPL
Use III
Use LOL
Use PPL
destroy III
destroy LOL
destroy PPL

但是没有显示的是unique_ptrs被创建、移动和销毁的速率与前面示例中的Customers相同。根据Customer的移动构造函数的成本,您可能会节省一些时间或精力,也可能不会。


如果你要列出所有可能的优化,那么如果你知道vector的大小,你应该首先提到使用reserve。这样可以避免所有重新分配和移动。 - Chris Drew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接