您的位置:首页 > 编程语言 > C语言/C++

C++ 内存管理

2014-01-10 03:42 169 查看

C++ 内存管理

关于析构函数

1. when the desconstructor will be called:

具体地说如果出现以下几种情况,程序就会执行析构函数:
①如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。
②static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。
③如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
④如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。

2. what does desconstructor do?

析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。程序设计者事先设计好析构函数,以完成所需的功能,只要对象的生命期结束,程序就自动执行析构函数来完成这些工作。

实际上,析构函数的作用并不仅限于释放资源方面,它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”,例如输出有关的信息。这里说的用户是指类的设计者,因为,析构函数是在声明类的时候定义的。也就是说,析构函数可以完成类的设计者所指定的任何操作。
一般情况下,类的设计者应当在声明类的同时定义析构函数,以指定如何完成“清理”的工作。如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数,但它只是徒有析构函数的名称和形式,实际上什么操作都不进行。想让析构函数完成任何工作,都必须在定义的析构函数中指定。

注意:析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。

观察下面的程序:

#include <cstdio>
#include <cstring>
#include <iostream>

class Chaine {
private:
char* _donnees;
unsigned int _taille;

public:
Chaine();
Chaine(const char*);
~Chaine();

unsigned int taille() const;
};

Chaine::Chaine():_taille(0) {
_donnees=new char[1];
_donnees[0]='\0';
}

Chaine::Chaine(const char *s) {
_taille = std::strlen(s);

_donnees = new char[_taille + 1];
std::strcpy(_donnees, s);

std::printf("%s(%d): %s\n", __FILE__, __LINE__, __func__);
}

Chaine::~Chaine() {
}

unsigned int Chaine::taille() const{
return _taille;
}

int main() {
//Chaine s1("une chaine"); //line 1
Chaine *s2 = new Chaine("s3"); //line 2     //Chaine s3 = s1; //line 3
delete s2; //line 4
}


编译 $ g++ -Wall -o test test.cpp

查看内存使用:$ valgrind --leak-check=full ./test

==4822== Command: ./test
==4822==
test.cpp(29): Chaine
==4822==
==4822== HEAP SUMMARY:
==4822== in use at exit: 3 bytes in 1 blocks
==4822== total heap usage: 2 allocs, 1 frees, 11 bytes allocated
==4822==
==4822== 3 bytes in 1 blocks are definitely lost in loss record 1 of 1
==4822== at 0x402A654: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==4822== by 0x80487C1: Chaine::Chaine(char const*) (in /home/qliang/Documents/ENSEIRB/cpp/td2/chaine2/test)
==4822== by 0x804883B: main (in /home/qliang/Documents/ENSEIRB/cpp/td2/chaine2/test)
==4822==
==4822== LEAK SUMMARY:
==4822== definitely lost: 3 bytes in 1 blocks
==4822== indirectly lost: 0 bytes in 0 blocks
==4822== possibly lost: 0 bytes in 0 blocks
==4822== still reachable: 0 bytes in 0 blocks
==4822== suppressed: 0 bytes in 0 blocks
==4822==
==4822== For counts of detected and suppressed errors, rerun with: -v
==4822== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

total heap usage: 2 allocs, 1 frees, 11 bytes allocated this is because:

with new Chaine("fs") we create an object in heap, with sizeof 11 bytes(8 bytes for data member _donnees 4 bytes and _taille 4 bytes, 3 bytes for the block of memory in heap that _donnees points to, which is created by using new char[_taille + 1] ).

Attention that, both the object itself(means its members) and the block of memory that the pointer members in the object points to are located in heap.

By contrast, the sentence line 1 creates an object in stack, not in heap! But, its member _donnees points to a block of memory in heap.

The delete operator just free the object members, but not the block of memory [b]its member pointer points to.[/b]

[b]Object in stack will be freed automatically when program terminates, the deconstructor will be automatically called by the compiler. But this doesn't apply to object in heap. We must use delete or delete[] to manully free that block of memory. You can use delete on stack object, but it's unnecessary. You will get warning like this:[/b]

test.cpp: In function ‘int main()’:
test.cpp:48:10: warning: the address of ‘s1’ will never be NULL [-Waddress]
delete &s1;
^


So, when program terminates, object s1 will cause a leak in memory. In order to avoid this situation, we can free that block of memory that _donnees points to in the deconstuctor. like:

Chaine::~Chaine() {
  delete[] _donnees;
  _donnees=NULL;
}


ATTENTION:

delete[] _donnees; this will free the block of memory _donnees points to. But the pointer _donnees itself still holds the address of that block of memory!!
to make that block of memory "clean"(other programs can use it without worring that this block of memory maybe modified by another program which has a pointer points to that memory!!!).
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: