最近一直对内存管理比较的模糊,因此专门学习了一下,下面是我的一些心得和体会拿来和大家一起分享如有不同的见解期望大家的指点。
我们都知道在C++内存管理中有5个分区(如有不知道的推荐阅读:http://www.cnblogs.com/qiubole/articles/1094770.html),也正因为种类太多才弄的比较的模糊。
为了方便区分我这里只用了堆和栈来识别他们的功能,事实上也是这么回事,广义的栈是指那些系统自动申请和回收的内存空间只在他们相应的作用域内存在;堆是指那些使用new/delete操作符以及malloc()/free()函数手动申请和释放的动态内存空间。在C++中想要安全而随意的使用这些东西并不是一件容易的事情,因为如果不是很熟练的话就容易造成内存泄露不像Java中的只需要new来申请引用类型空间释放就是JVM调用GC的事情了,当然如果你牛逼的话就会感觉到这些工具的强大。
还是来个实例更好的解释:参数传递机制。
参数的传递实际上就是将实参给形参(临时变量存在栈内存空间里)初始化,同样函数返回也是同样的效果用函数返回值来赋值给左操作数(对于引用就是右操作数来修改函数的返回变量)。
注意在下面的例子中指针引用就像是java类型引用是一个指向内存空间的地址,C++引用就是一个别名:
int oi = 1;
int* opi = &i;
void f(int ii){//修改ii}
void g(int* ipi){//修改ipi}
f(oi);//相当于int ii = oi;修改ii不影响oi的值,在函数返回之后系统回收ii的内存空间
f(*opi);//相当于int ii = *opi;修改ii不影响opi
g(opi);//相当于int* ipi = opi;修改ipi不影响opi的值,因此函数结束的时候回收ipi时不会影响opi,但是如果ipi值不修改而直接修改*ipi那么就会修改oi的值
int* h(){int temp = 0;return &temp;}
opi=h();//试图返回一个指向栈内存空间的指针引用,首先return让opi得到了&temp但是函数结束后会回收栈的内存空间那么temp就会无效同样&temp也就无效了
int& m(int& temp){return temp;}
m(oi) = 0;//试图返回一个栈内存变量的别名引用来修改返回变量,首先int& temp = oi;进行引用绑定然后返回temp赋给外部引用然后在进行修改,但是返回temp之后函数结束了就会回收这部分内存同样外部引用就无效了。
接下来看看new的使用:
int* pc = new char[5];//意思就是用new来申请5个char型的动态内存空间并返回首地址赋给栈内存变量pc,在变量的作用域结束的时候堆的内存空间需要自己手动释放
delete []pc;//是指释放pc的值指向的堆内存空间,然后随机重新给pc赋值一个未知地址!
pc= NULL;//让pc指向一个安全的地址NULL
在自定义类型实例化的时候调用构造函数在对象结束的时候系统自动调用默认析构函数,如同上面的栈内存变量!但是如果自定义类型使用了new来申请堆内存空间的话那么默认析构函数就不会调用delete了因此要自己重写析构函数来释放堆内存
template<class T >
class TestClass
{//simple constructor
public:
TestClass():_p(0){}
TestClass(const T & p):_p(new T(p)){}//重写operator new()
~Test(){delete _p;}//重写operator delete()
//new and delete operator
Void * operator new(size_t size);
Void operator delete();
//other members here
private:
T * _p
};
Template<class T>
Void * TestClass<T>::operator new(size_t size)
{
Void * p=malloc(size);
Return p;
}
Template<class T>
Void TestClass<T>::operator delete()
{
Free(_p);
}
int main()
{
TestClass<int> ptr = TestClass<int>(1);
//使用ptr
}
上面的ptr是栈内存变量在程序结束的时候系统自动调用析构函数回收它的内存,同样调用构造函数的时候申请了堆内存空间调结束的时候调用了析构函数释放了这块堆内存空间。