c++面向对象高效编程 pdf,c++面向对象编程实例
详细讨论了C资源管理的思想:RAII技术。
目录
1.概述2。详细讨论2.1。堆,栈,静态区2.2。手动管理资源的缺点。间接使用。自底向上抽象3。总结4。参考
1.概述在前两篇文章《面向对象编程(C++篇2)——构造》和《面向对象编程(C++篇3)——析构》中,我们讨论了C面向对象的一种更好的实现,在构造函数中申请动态内存,在析构函数中释放。这样我们就可以像类对象有内置的数据类型一样,自动实现对象的生命周期管理。
其实这个设计早就被C之父比雅尼斯特劳斯特鲁普提出来了,叫做RAII(资源获取即初始化),中文意思是资源获取即初始化。上面说的动态内存只是资源的一种,比如文件的打开和关闭,windows中句柄的获取和释放等等。RAII是一个随意的名字,但是这个技术可以说是C的基石,决定了C资源管理的方方面面。
2.详细讨论2.1。堆、栈、静态区域。更深入的说,RAII实际上是在自己的实际程序中利用堆栈的特性来实现资源的自动化管理。我们知道,通用程序分为三个存储区:
静态内存:用于存储本地静态对象、静态数据成员和任何函数外部定义的任何变量。堆栈内存:用于存储函数中定义的非静态对象。堆内存:用于存储动态分配的对象,比如通过new、malloc等应用的内存对象。对于分配在静态内存中的对象和堆栈内存中的对象,它们的生命周期由编译器自动创建和销毁。对于堆内存来说,生命周期是由程序显式控制的,使用后需要通过delete来释放。我们通过在堆栈中分配类对象的RAII机制来管理在堆空间中分配的内存:
ImageEx类
{
公共:
ImageEx()
{
cout 执行构造函数!endl
data=新的无符号字符[10];
}
~ImageEx()
{
发布();
cout“执行析构函数!”endl
}
私人:
无符号字符*数据;
无效释放()
{
删除[]数据;
data=nullptr
}
};
int main()
{
{
ImageEx imageEx
}
返回0;
}显然,根据程序栈的内存需求,ImageEx对象一旦离开作用域,就会自动调用析构函数,从而实现资源的自动管理。
2.2.手工管理资源的弊端在古代的C/C中,程序员不得不按照‘谁申请,谁释放’的原则,小心翼翼地管理资源。但实际上,这并不能总是避免内存泄漏的问题。一个非常常见的例子如下(这是一个函数ImageProcess(),它“处理图像中的像素”):
int doSomething(int* p)
{
return-1;
}
布尔图像处理()
{
int * data=new int[16];
int error=do something(data);
如果(错误)
{
删除数据;
data=nullptr
返回false
}
删除数据;
data=nullptr
返回true
}为了避免内存泄漏,我们必须释放这个函数中任何可能出错并返回之前的地方的内存。这无疑是低效的。并由RAII技术改写如下:
int doSomething(ImageEx imageEx)
{
return-1;
}
布尔图像处理()
{
ImageEx imageEx
if (doSomething(imageEx))
{
返回false
}
返回true
}这时候我们就完全不用关心动态内存资源的释放了,因为类对象超出范围后,它调用析构函数自动释放应用的动态内存。无论是代码量还是编程效率,都有了很大的提升。
2.3.间接使用。可以肯定的是,无论用什么样的操作(删除、析构函数、普通函数)来释放内存资源,都会给程序员带来精神负担。最好不要手动释放内存资源。要是能交给程序自动管理就好了。对此,现代C给出的解决方案是RAII。
在现代C中,动态内存推荐使用智能指针类型(shared_ptr,unique_ptr,weak_ptr)来管理动态内存对象。智能指针采用引用计数的RAII来动态管理它所指向的内存资源。当引用计数为0时,它将自动释放它所指向的内存对象。
对于动态数组,现代C推荐stl容器,尤其是std:vector容器。Std:vector容器是一个模板类,也是基于RAII实现的,其应用的内存资源也会在超出范围时自动析构。
所以智能指针和stl容器的使用,也就是RAII的间接使用,意味着我们不用担心释放资源的问题。
2.4.自下而上的抽象当然实际情况可能没那么好。在程序的底层,可能还存在一些需要管理的资源,或者访问第三方库(尤其是C库)。他们仍然手动管理内存,也许我们不能使用智能指针或stl容器。但是我们仍然可以使用RAII来一步一步地抽象封装,例如:
ImageEx类
{
公共:
ImageEx()
{
cout 执行构造函数!endl
data=新的无符号字符[10];
}
~ImageEx()
{
发布();
cout“执行析构函数!”endl
}
私人:
无符号字符*数据;
无效释放()
{
删除[]数据;
data=nullptr
}
};
类别纹理
{
公共:
texture()=default;
私人:
ImageEx imageEx
};
int main()
{
{
纹理纹理;
}
返回0;
} ImageEx可以认为是底层类,需要动态内存管理但不能使用std:vector,所以我们采用RAII来管理;Texture是一个高级类,内部有ImageEx数据成员。此时,我们可以发现没有必要显示和破坏纹理类。当纹理离开范围时,它会自动销毁ImageEx数据成员并调用其析构函数。也就是说,纹理对象已经完全忽略了内存资源释放的问题。
那么可以得出一个结论:如果底层不能使用智能指针或者stl容器来自动管理资源,最多只要第一层的底层采用RAII设计,那么高层类就不需要显示和析构。这样一个完美的世界出现了:程序员确实自己管理资源,但不需要成本,或者只需要很小的成本(真正需要手动管理资源时采用RAII机制),这就使得这种管理自动化了。就像一门有GC(垃圾收集)机制的编程语言,程序员可以随意申请资源,不关心资源释放。
3.总结无论是哪种编程语言,资源管理都是一个非常严肃的话题。对于资源管理,现代C给出的答案是RAII。通过这项技术,减少了内存泄漏的可能性和手动管理资源的精神负担。同时自动管理资源,性能要求也得到了保证。当然,这也是C‘零开销抽象’设计理念的体现。
4.参考C中的RAII介绍RAII:如何用C编写无内存泄漏的代码。
目录
下一篇文章
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。