STL删除元素时会清理哪些东西?

来源:互联网 发布:淘宝号码 编辑:程序博客网 时间:2024/05/16 11:17

        stl容器删除元素时,大多数人都认为应该会将该元素的内容进行清除,如果该元素是一个类,那么会调用这个类的析构函数。确实,当元素为STL容器的对象时,的确会调用这个类的析构函数,但是当元素为对象指针,而不是对象时,那么久不会调用析构函数。

        我们使用一个类来进行说明,使用set作为stl容器。

#include<stdio.h>
#include<set>

using namespace std;

class TestClass
{
public:
TestClass(){ printf("TestClass()\n");}
~TestClass(){ printf("~TestClass()\n");}
bool operator<(const TestClass &item) const{ ;}
};

typedef set<TestClass> TestSet;
typedef set<TestClass*> TestSet1;

int main()
{
TestClass *t1 = new TestClass();
TestSet1 testset1;
testset1.insert(t1);
testset1.erase(t1);

return 0;
}

        上面的代码只会输出一行TestClass(),显然上面的代码是有缺陷的,在代码最后面少了delete t1的操作,导致指针没有释放。这里举这个例子的意图仅仅是为了说明在调用erase时,并没有调用TestClass的析构函数,仅此而已。

        那么当元素不为指针,为真正的对象的时候,情况又会怎么样呢,我们看下面的代码,只是改变了main函数

int main()
{
TestClass t;
TestSet testset;
testset.insert(t);
testset.erase(t);

return 0;
}

        输出为:

TestClass()
~TestClass()
~TestClass()

        竟然调用了2次析构函数!!!

        在最开始的时候我也是很莫名其妙,在这两次析构函数的调用中,testset.erase(t);这段代码贡献了一次,这个大家都能够理解,毕竟stl在销毁对象时,必然会调用析构函数。但是对于指针,却不调用析构函数,那是因为指针仅仅表示指向实体的一个地址的变量而已,并不代表实体本身,所以删除了指针并不代表删除了实体,而析构函数默认只会在实体被删除/销毁的时候调用,所以删除指针并不调用析构函数。

        对于第2次析构函数的调用,我认为是程序退出时清理临时变量的时候调用的。由于我不懂二进制代码的汇编程序,还不明白为什么C++编译器会在临时对象呗销毁之后还要销毁第2次。

        既然对象要销毁2次对象,那么我们就必须加倍小心了。看下下面的代码。

#include<stdio.h>
#include<set>

using namespace std;

class TestClass
{
public:
TestClass(){ printf("TestClass()\n"); pc = new char[100];}
~TestClass(){ printf("~TestClass()\n"); delete pc;}
bool operator<(const TestClass &item) const{ ;}

private:
char * pc;
};

typedef set<TestClass> TestSet;
typedef set<TestClass*> TestSet1;

int main()
{
TestClass t;
TestSet testset;
testset.insert(t);
testset.erase(t);

return 0;
}

        上面的代码仅仅增加了内存的分配操作,在析构函数释放分配的内存。大家可能已经料想到,在运行上面的代码的时候,必然会引起段错误。那是因为析构函数执行了2次,从而引起释放已经释放的内存的错误:double free or corruption。

        在上面的例子中可以看出,如果某个类是STL容器的元素类的话,千万不要在这个类的析构函数中执行如内存释放之类的操作,这样的代码很容易引起段错误。