首页 > 代码库 > 编写高质量代码——“零星”总结(续2)
编写高质量代码——“零星”总结(续2)
newdelete与new[]delete[]必须配对使用
//注意,由于内置数据类型没有构造、析构函数,所以针对内置数据类型时,释放和内存使用delete或delete[]的效果是一样的。
例如:int *pArray = new int[10];
...//processing code
delete pArray; //等同于delete[] pArray;
虽然针对内置类型,delete和delete[]都能正确地释放所申请的内存空间,但是如果申请的是一个数组,建议还是使用delete[]形式。
===================================
new内存失败后的正确处理
//新的标准里,Test-for-NULL处理方式不再被推荐和支持:抛出一个bad_alloc exception (异常)
//推荐使用 try{ p = new int[SIZE]; } catch ( std::bad_alloc ) { ... }
//只有负责内存申请的 operator new 才会抛出异常 std::bad_alloc。
===================================
new内存失败后的正确处理
了解new_handler的所作所为
借助工具监测内存泄漏问题
小心翼翼地重载operator new operator delete用智能指针管理通过new创建的对象
===================================
使用内存池技术提高内存申请效率与性能
//当程序中需要对相同大小的对象频繁申请内存时,常会采用内存池(Memory Pool)技术来提高内存申请效率,减少内存碎片的产生。
----------------------------------
//基本原理(“池”):应用程序可以通过系统的内存分配调用预先一次性申请适当大小的内存块(Block),并且将它分成较小的块(Smaller Chunks),之后每次应用程序会从先前已经分配的块(chunks)中得到相应的内存空间,对象的分配和释放的操作都可以通过这个“池”完成。只有当“池”的剩余空间太小,不能满足应用程序需要时,应用程序才会在调用系统的内存分配函数对其大小进行动态扩展。(和malloc差不多,进行一次的系统调用,分配33页,不够再申请)
----------------------------------
//经典的内存池实现原理:指针变量pMem把所有申请的内存块(MemBlock)串成一个链表。指针变量pFree把所有自由的内存结点(FreeNode)串成一个链表。 内存块在申请之初就被划分为了多个内存结点,每个结点的大小为ItemSize(对象的大小),共计MemBlockSize/ItemSize个。//每次分配的时候从MemBlock链表中取一个结点给用户,不够时继续向系统申请大块内存。
//在释放内存时,只须把要释放的结点添加到自由内存链表pFree中即可。
//在MemPool对象析构时,可完成对内存的最终释放。
===================================
明晰class与struct之间的区别
//面向过程的编程认为,数据和数据操作应该是分开的。
//面向对象的C++认为,数据和数据的操作是一个整体。
===================================
了解C++悄悄做的那些事
//所有的类都有着一个类似的中枢骨干(“Big Three”):一个或多个构造函数 + 一个析构函数 + 一个拷贝赋值运算符
//=delete =default
//对于类,编译悄悄完成的事:隐式产生默认构造函数、拷贝构造函数、拷贝赋值运算符和析构函数。
//为了能够实例化,编译器“强制”使其大小由 0 变成 1。
===================================
明智地拒绝对象的复制操作
//禁止对象复制操作(拷贝构造函数 和 赋值操作符):
方式一、通过类的访问控制private(一方面阻止了编译器越俎代庖的声明和定义,另一方面禁止对复制操作函数的显示调用)
如下:
class CStudent{
public :
...
private :
CStudent ( const CStudent& other );
CStudent operator=( const CStudent& rhs );
}//但是,成员函数和友元函数仍可以访问,C++赋予了它们调用 private 成员函数的权利。
---------------------------------
方式二、只声明不定义,那么成员函数和友元函数对它们的调用,编译器会明令禁止(声明而不定义成员函数是合法的,但是使用未定义成员函数的任何尝试将导致链接失败),即实现了类复制的完全禁止。
================================
小心,自定义拷贝函数(拷贝构造函数和赋值运算符)
//自定义拷贝函数是一种良好的编程风格,可以阻止编译器形成默认的拷贝函数,但应该保证拷贝一个对象的 All Parts:所有数据成员及所有的基类部分。
==========
======================
多态基类的析构函数应该为虚
//C++中指出:当一个派生类对象通过使用一个基类指针删除,且这个基类有一个非虚的析构函数时,C++将不会调用整个析构链,只是调用了基类的析构函数,即“部分析构”。
--------------------------------
//解决:将基类的析构函数设置为虚,用基类指针删除一个派生类对象时,C++会正确地调用整个析构链,销毁整个对象。
--------------------------------
//使用虚析构函数要遵守的原则:(基)类中至少有一个虚函数。反之,如果类中有虚函数,它就该有一个虚析构函数。
//如果一个类不包含虚函数,不要将析构函数声明为虚。因为虚函数的实现要求对象携带额外的信息,会导致对象的大小增加。
--------------------------------
//普通继承:自动调用基类和派生类的析构函数。
//多态:有多态,基类一定有虚成员函数,有虚成员函数,析构函数就要声明为virtual。
================================
绝不让构造函数为虚
//假设构造函数是虚函数,那么就需要通过虚函数表来调用应该调用的函数,但此时面对的是一块 raw 内存,虚函数表尚未建立,不能支持虚函数机制(虚函数表由构造函数建立),故构造函数不能为虚函数。
================================
默认参数在构造函数中给你带来的喜与悲
//如果不合理的使用默认参数,将会导致重载函数的二义性。
【好烦CSDN的字体问题a】