C++学习之六、有效的内存管理(二)

2014-11-24 12:34:42 · 作者: · 浏览: 8

C风格的字符串的优缺点:

优点:1.比较简单,利用了底层的基本字符类型和数据结构。

2.占用空间小,如果正确使用,它们只需占用真正需要的内存空间。

3.更底层,所以可以作为原始内存很容易地进行处理和复制。

4.程序员能够更好地理解。

缺点:

1.不能忍受内存bug的存在,而且很受其影响。

2.没用充分利用C++面向对象特性。

3.提供的辅助函数命名很糟糕,有时还会把人搞糊涂。

4.要求程序员了解字符串的底层表示。

C++的字符串类string:

基于操作符重载的魔力,string使用+连接二个字符串,=进行赋值(会进行字符串复制),==进行比较,[]进行访问单个字符。

可以使用c_str()把C++string转化为C风格的字符串

低级的内存操作:

C++的主要优点之一就是不需要特别担心关于内存的问题。如果代码用到了对象,只需要确保各个类能适当地管理它自己的内存即可。通过构造和撤销对象,编译器会告诉你什么时候做什么,从而帮助你管理内存。但是出于某些应用,可能会遇到这种情况,即需要在低层次上使用内存。

指针运算:指针加1是指针向前移动一个单位。同类型指针减是二指针之间的元素的个数。

如果编一个把字符串转换为大写,char *toCaps(const char * inString);

如果只想想把字符串myStr的后面转换为大写,则可以这样调用 toCaps(myStr+5);

自定义内存管理:大多数情况之下,内置的内存分配功能就已经足够了,但是如果资源要求紧张,就完全可以自己来管理内存。自己管理内存可能会减少资源开销。使用new分配内存时,程序还需要保留一小块空间来记录已经分配了多少内存空间。这样,调用delete时,就可以释放适当数量的内存。对于大部分对象,相对于分配的内存来说,这个开销要小得多,所以并没有太大的区别。然而,对于小的对象或者有大量对象的程序,这种开销可能会有较大的影响。自己管理内存时,你事先已经知道每个对象的大小,所以可以避免这个开销。对于大量的小对象来说,与使用new和delete的方法相比,这样就会带来很大的差别。

垃圾回收:在支持垃圾回收的环境中,程序员很少需要显式地释放与对象关联的内存。取而代之的是,有一个低优先级的后台任务负责监视内存状态,清理它认为不需要的内存。

不同于java语言,在C++中,没有把垃圾回收作为内置功能。大部分的C++程序通过new和delete在对象层次上管理内存。在C++中实现垃圾回收也不是不可能,但是要想从释放内存的任务中解脱出来,可能又会带来新的问题。

垃圾回收的一种方法称为标记和清扫。使用这种方法,垃圾回收器会周期性地检查程序中得每个指针,并标记所引用的内存仍在使用。在循环结束时,没有标记的内存就认为未在使用,可以释放。

需要完成的步骤:

1. 向垃圾回收器注册所有的指针,这样就可以很容易地扫描整个指针列表。

2. 让所有对象派生一个混合类(如GarbageCollectible),它允许垃圾回收器把对象标记正在使用。

3. 确保垃圾回收器运行时不会对指针做修改,以此来保护对对象的并发访问。

这个简单的垃圾回收方法要求程序员很仔细才行。与delete相比,这种方法可能更容易带来错误。在C++中已经试图建立一种安全而容易的机制来完成垃圾回收,但是即使在C++确实提供了一个理想的垃圾回收实现,也不一定适用于所有的应用。

垃圾回收存在以下缺点:

1. 垃圾回收器主动运行时,可能会使程序的运行减慢。

2. 如果程序大量地分配内存,那么垃圾回收器可能跟不上这个速度。

3. 如果垃圾回收器本身有bug,或者认为一个已经抛弃的对象仍然在使用,可能会造成不可恢复的内存泄露。

对象池:后面再分析。

函数指针:每个函数的确都位于一个特定的地址。在C++中,可以把函数作为数据使用,换句话说,可以把函数的地址作为参数,就想变量一样使用。

函数指针根据参数类型和兼容函数的返回类型来确定函数类型。使用函数指针最容易的方法就是使用typedef机制来为一组有给定特征的函数赋一个函数名。下面声明了一个类型YesNoFcn,它表示一个指针,该指针指向有二个int参数且返回bool类型的任意函数。

typedef bool (*YesNoFcn)(int,int);

既然有了新类型,就可以编写一个取YesNoFcn作为参数的函数了。

void findMatches(int values1[],int values2[],int numValues,YesNoFcn inFunction)

{

for(i=0;i

if(inFunction(values1[i],values[2]))

cout<<”match! “;

else cout<<”not match! “;

cout<

}

bool intEqual(int inItem1,int inItem2)

{

return inItem1==inItem2;

}

//调用

int a[2]={1,2},b[2]={1,3};

findMatches(a,b,2,&intEqual);

常见的内存陷阱:

1. 字符串空间分配不足

char str[3] = “yes”; //error 还有一个’\0’

2. 内存泄露

如果分配了内存,但是忘记了释放,此时就容易产生内存泄露。(跟踪内存使用情况可以使用免费的valgrind工具)。

也可以使用智能指针避免内存泄露。即如果把一切都放在栈中,这就可以避免与内存相关的大多数问题。栈比堆更安全,因为栈变量一旦超出作用域时就会自动撤销和清除。智能指针结合了栈变量的安全性和堆变量的灵活性。它是一个带有关联指针的对象。当智能指针超出作用域时,会删除关联的指针。本质上讲就是在一个基于栈的对象内包装一个堆对象。

C++标准模板库包含了一个智能指针的基本实现。叫做auto_ptr。可以把动态分配的对象存储在基于栈的auto_ptr实例中,而不是存储在指针中。不需要显式地释放与auto_ptr关联的内存-auto_ptr超出作用域时,与之关联的内存会得到清除。

void leaky()

{

Simple * mySimple = new Simple();

mySimple->go();

}//没有显式地释放内存,删除对象。

void leaky()

{

auto_ptr mySimple(new Simple);

mySimple->go();

}

智能指针也和标准指针一样可以使用* ->来解除引用。

3.二次删除与无效指针

一旦使用delete释放了与指针关联的内存,程序中得其他部分就可以使用这段内存了。但是,没有什么能阻止你试图继续使用这个指针。二次删除也是一个问题