std::vector.clear()会对每个元素执行delete(释放内存)操作吗?

69

考虑以下代码:

#include <vector>

void Example()
{
    std::vector<TCHAR*> list;
    TCHAR* pLine = new TCHAR[20];
    list.push_back(pLine);
    list.clear();    // is delete called here?
    // is delete pLine; necessary?
}

list.clear()是否会调用每个元素的delete函数?也就是说,在/之后调用list.clear()之前,我是否需要释放内存?

6个回答

55

std::vector在调用clear()时会调用其包含的每个元素的析构函数。在你的情况下,它将销毁指针,但对象仍然存在。

智能指针是正确的选择,但要小心。在std容器中无法使用auto_ptr,也无法使用boost::scoped_ptr。可以使用boost::shared_ptr,但在你的情况下它不起作用,因为你没有一个指向对象的指针,而是使用了一个数组。因此,解决你的问题的方法是使用boost::shared_array

但我建议你使用std::basic_string<TCHAR>,这样你就不必处理内存管理,同时还可以获得与字符串一起工作的好处。


好主意,可以随意将这些细节编辑到我的答案中...(我有点生疏我C++了,我很尴尬没有提到basic_string!) - Ruben Bartelink
说到内存管理,我有点控制狂。我生活在嵌入式(手机)开发的世界中。使用部分受控 API 让我感到毛骨悚然。UI 下面的层是 ANSI C。 - Ignas Limanauskas
你认为“部分管理”是什么?shared_array还是basic_string?无论哪种方式,我认为它都非常完整!如果你想控制内存管理,我不会首先使用std::vector。它可能会在没有告诉你的情况下保留内存! - Benoît
@Benoit:哦,是的,我给了你10分以引导你朝着3k的方向前进! @Ignas:值得探索托管的东西和RAI模式的普遍应用(我在.NET上研究了几年,但即使在那之前,我也写过我的最后一个删除/释放操作很多年!) - Ruben Bartelink
不一定。如果你想确保 std::vector 不会在你没有要求的情况下预留空间,可能有一些 stlport 的编译选项可以实现这个功能。但我不确定! - Benoît
显示剩余4条评论

42
没有(你需要自己在最后进行删除,就像你在示例中建议的那样,因为裸指针的销毁并没有做任何操作)。但是,你可以使用 boost [或其他基于 RAII 的习语] 智能指针来使它做正确的事情(auto_ptr 在容器中不会正常工作,因为它在复制等行为下具有不兼容的行为),但一定要确保在使用此类智能指针之前了解其陷阱。(正如 Benoit 所提到的,在这种情况下,basic_string 是你真正需要的东西。)
话虽如此,需要理解智能指针的潜在问题,通过隐式地处理内存管理,使你无需显式地进行处理,从而减少错误的发生率。
编辑:大大修订,涵盖了 Benoit 在他的回答中提到的元素,感谢 Earwicker 和 James Matta 的强烈督促 - 感谢你们推动我进行尽职调查!

4
auto_ptr 不能使用。当与 STL 容器一起使用时,auto_ptr 会出现问题。 - James Matta
你应该编辑答案,删除auto_ptr的引用。实际上无法在容器中使用它。 - Daniel Earwicker
@JM 删除之前的乱序评论,我以为我是谁! - Ruben Bartelink

9
这里有一种方法可以判断它是否可行 - 尝试在一个未完全定义的类上使用它:
#include <vector>
class NotDefined;

void clearVector( std::vector<NotDefined*>& clearme )
{
    clearme.clear();    // is delete called here?
}

如果这段代码能够编译通过,那么它不可能调用析构函数,因为析构函数没有被定义。

我认为你的回答可能对于这个特定情况是正确的,但并不适用于一般情况。我想到了 SFINAE 或者 https://en.cppreference.com/w/cpp/types/is_destructible,它们可以在定义了析构函数时调用析构函数。 - Et7f3XIV

8
您可以编写一个简单的模板函数来实现此功能:
template <class T>
void deleteInVector(vector<T*>* deleteme) {
    while(!deleteme->empty()) {
        delete deleteme->back();
        deleteme->pop_back();
    }

    delete deleteme;
}

也许这里有一些不好的做法,但我认为没有。对我来说看起来还不错,尽管注释总是很好的。

1
这并没有真正解决问题。如果list.push_back(pLine)抛出异常,内存仍然会泄漏。正确的做法是使用std::vector<TCHAR>而不是TCHAR* - Mankarse
2
这是一种糟糕的方法。到处使用指针会使程序变得极其缓慢。如今编写快速程序的关键是始终使用连续的内存,而最好的方法是直接使用vector<T>,其中T不是指针。 - Dom

8

不行。由于无法保证您没有在其他地方使用指针,因此它不会执行此操作。如果它不是一个指针变量,则会释放它们(通过调用析构函数)。


8
std::vector怎么知道如何删除指针?它可能是用new、malloc或其他任何内存分配函数(特定于操作系统,手动编写)分配的,从类型上无法判断。 - Niki

0
你也可以尝试使用Boost Pointer Container Library。虽然这里并不特别推荐(因为你使用的是数组而不是单个对象,但如果使用std::string就可以解决这个问题),但它是一个有用且鲜为人知的库,可以解决标题中提到的问题。

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