Effective C++摘要《1、2章:从c转向c++、内存管理》20090206

本文介绍了C++编程中的一些最佳实践,包括使用const和inline代替#define、使用new/delete替代malloc/free、处理内存分配失败的方法等。文章通过具体示例阐述了这些技巧的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

===条款1:尽量用const和inline而不用#define===
这个条款最好称为:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分
有了const和inline,你对预处理的需要减少了,但也不能完全没有它。抛弃#include的日子还很远,#ifdef/#ifndef在控制编译的过程中还扮演重要角色。
===条款2:尽量用<iostream>而不用<stdio.h>===
iostream库的类和函数所提供的类型安全和可扩展性的价值远远超过你当初的想象
===条款3:尽量用new和delete而不用malloc和free===
malloc和free(及其变体)会产生问题的原因在于它们太简单:他们不知道构造函数和析构函数。
string *stringarray1 = static_cast<string*>(malloc(10 * sizeof(string)));
string *stringarray2 = new string[10];

其结果是,stringarray1确实指向的是可以容纳10个string对象的足够空间,但内存里并没有创建这些对象。而且,如果你不从这种晦涩的语法怪圈里跳出来的话,你没有办法来初始化数组里的对象。换句话说,stringarray1其实一点用也没有。
相反,stringarray2指向的是一个包含10个完全构造好的string对象的数组,每个对象可以在任何读取string的操作里安全使用。
===条款4:尽量使用c++风格的注释===
if ( a > b ) {
 /* int temp = a;  /* swap a and b */    错误!
  a = b;
  b = temp;
 */
}
===条款5:对应的new和delete要采用相同的形式===
string *stringptr1 = new string;
string *stringptr2 = new string[100];
...
delete stringptr1;// 删除一个对象
delete [] stringptr2;// 删除对象数组

如果你在stringptr1前加了"[]"会怎样呢?答案是:那将是不可预测的;如果你没在stringptr2前没加上"[]"又会怎样呢?答案也是:不可预测。
而且对于象int这样的固定类型来说,结果也是不可预测的,即使这样的类型没有析构函数。//这点需要注意
===条款6:析构函数里对指针成员调用delete===
删除空指针是安全的,所以,在写构造函数,赋值操作符,或其他成员函数时,类的每个指针成员要么指向有效的内存,要么就指向空,那在你的析构函数里你就可以只用简单地delete掉他们,而不用担心他们是不是被new过。
===条款7:预先准备好内存不够的情况===
operator new在无法完成内存分配请求时会抛出异常
当operator new不能满足请求时,会在抛出异常之前调用客户指定的一个出错处理函数——一般称为new-handler函数

处理内存分配失败的情况时采取什么方法,取决于要分配的对象的类:即在类中定义new-handler函数
class x {
public:
 static void outofmemory();
 ...
};

int main()
{
    x::set_new_handler(x::nomorememory); // 把nomorememory设置为x的new-handling函数
    x* p1 = new x; // 若分配成功,调用x::outofmemory
    x::set_new_handler(0); // 设x的new-handling函数为空
}

类实现
class x {
public:
 static new_handler set_new_handler(new_handler p);
 static void * operator new(size_t size);
 
private:
 static new_handler currenthandler;
};

new_handler x::set_new_handler(new_handler p)
{
 new_handler oldhandler = currenthandler;
 currenthandler = p;
 return oldhandler;
}
void * x::operator new(size_t size)
{
   new_handler globalhandler =  // 安装x的new_handler   
    std::set_new_handler(currenthandler);
    
    void *memory;
   
    try {  // 尝试分配内存
       memory = ::operator new(size);
    }
   
    catch (std::bad_alloc&) {  // 恢复旧的new_handler
       std::set_new_handler(globalhandler);     
       throw; // 抛出异常
    }
   std::set_new_handler(globalhandler); // 恢复旧的new_handler
   return memory;
}

方法重用:继承+模板
template<class t> // 提供类set_new_handler支持的
class newhandlersupport { // 混合风格”的基类
public:
 static new_handler set_new_handler(new_handler p);
 static void * operator new(size_t size);

private:
 static new_handler currenthandler;
};

template<class t>
new_handler newhandlersupport<t>::set_new_handler(new_handler p)
{
 new_handler oldhandler = currenthandler;
 currenthandler = p;
 return oldhandler;
}

template<class t>
void * newhandlersupport<t>::operator new(size_t size)
{
 new_handler globalhandler =
  std::set_new_handler(currenthandler);
 void *memory;
 try {
  memory = ::operator new(size);
 }
 catch (std::bad_alloc&) {
  std::set_new_handler(globalhandler);
  throw;
 }
 
 std::set_new_handler(globalhandler);
 return memory;
}
// this sets each currenthandler to 0
template<class t>
new_handler newhandlersupport<t>::currenthandler;

有了这个模板类,对类x加上set_new_handler功能就很简单了:只要让x从newhandlersupport<x>继承:
// note inheritance from mixin base class template. (see
// my article on counting objects for information on why
// private inheritance might be preferable here.)
class x: public newhandlersupport<x> {

...  // as before, but no declarations for
};  // set_new_handler or operator new
===条款8: 写operator new和operator delete时要遵循常规===
1、要有正确的返回值
2、用内存不够时要调用出错处理函数
3、处理好0字节内存请求的情况
4、免不小心隐藏了标准形式的new
===条款9: 避免隐藏标准形式的new===
class x {
public:
  void f();

  // operator new的参数指定一个
  // new-hander(new的出错处理)函数
  static void * operator new(size_t size, new_handler p);
};

void specialerrorhandler();          // 定义在别的地方

x *px1 =  new (specialerrorhandler) x;       // 调用x::operator new
x *px2 = new x;   // 错误!

解决办法:
1、在类里写一个支持标准new调用方式的operator new,它和标准new做同样的事。
2、为每一个增加到operator new的参数提供缺省值

===条款10: 如果写了operator new就要同时写operator delete===
class airplane {
public:
  ...
private:
  airplanerep *rep;             // 指向实际描述
};
一个airplane对象并不大,它只包含一个指针但当调用operator new来分配一个airplane对象时,得到的内存可能要比存储这个指针(或一对指针)所需要的要多。
之所以会产生这种看起来很奇怪的行为,operator new和operator delete之间需要互相传递信息
当你写了下面的语句,
airplane *pa = new airplane;
你不会得到一块看起来象这样的内存块:
    pa——> airplane对象的内存
而是得到象这样的内存块:
    pa——> 内存块大小数据 + airplane对象的内存

如果airplane中写了operator new
airplane *pa = new airplane;        // 调用airplane::operator new
...
delete pa;                          // 调用 ::operator delete
那么operator new(在airplane里定义的那个)返回了一个不带头信息的内存的指针pa,而operator delete(缺省的那个)却假设传给它的内存包含头信息。这就是悲剧产生的原因。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值