Effective C++ 条款26:尽可能延后变量定义式的出现时间

  1. 考虑到抛异常的情况

2. “直接在构造函数时指定初值”比“通过default构造函数构造出一个对象然后对它赋值”效率更高,同时“具有明显意义之初值”将变量初始化,还可以附带说明变量的目的。总之就是尽量避免无意义的行为。

3.

// A:w定义于循环外
Widget w;
for (int i = 0; i < n; ++i) {
    w = i * 2;
    ...
}

// B:w定义于循环内
for (int i = 0; i < n; ++i) {
    Widget w;
    w = i * 2;
    ...
}

A : 一个构造 + 一个析构 + n个赋值

B:n个构造 + n个赋值

如果对象的一个赋值成本低于一组构造+析构成本,A高效,尤其是n较大时。A造成w的作用域比B大,这是缺点。

因此除非(1)你知道赋值成本比“构造+析构”成本低,(2)你正在处理代码中效率高度敏感的部分,否则应该用做法B。

------------------------------------------------------------------------------

实践:设计一个类CLog用来记录错误日志,可能不会用到,可能是全局都要使用的,希望尽可能延后出现的时机。

办法1:如果先定义一个指针,CLog* pLog = NULL; 需要时再new出一个对象来,顺便说句,new其实是做了malloc+构造的工作。详见

下面的链接,讲了很清楚,尽管我不懂汇编。new内部在做些什么----小话c++(6)

这种办法要考虑new失败的情况。

办法2:

CLog* getLog(){

static CLog log;

return &log;

}

第一次调用getLog()时CLog的构造函数才会执行。

C中如 malloc 等分配的内存在堆中分配。初始化了的静态变量和全局变量放在Data段中。未初始化的全局变量和局部静态变量放在Bss段中,更准确的说是在Bss段为它们预留了空间。非静态局部变量是在函数调用过程中暂存在栈上的。因此办法1中new分配内存失败的问题办法2木有。

但是还是要考虑到多线程的问题,也许可能会在多个线程上同时执行构造函数,导致内存泄露。

办法3:

char buf[sizeof(CLog)+ sizeof(int)];

CLog* pLog = NULL;

...

pLog = new(buf)CLog;

此为placement new,这办法先分配内存,构造函数则在需要时进行。

------------------------------------------------------------------------------

关于placement new,http://blog.csdn.net/zhangxinrun/article/details/5940019

placement new的作用就是:创建对象(调用该类的构造函数)但是不负责分配内存,而是在已有的内存块上面创建对象。用于需要反复创建并删除的对象上,可以降低分配释放内存的性能消耗。

placement new 的原型:void operator new( size_t, void p ) throw() { return p; }

void*p指向一个已经分配好的内存缓冲区的的首地址。

我们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。

placement new 使用方法如下:

  1. 缓冲区提前分配

可以使用堆的空间,也可以使用栈的空间,所以分配方式有如下两种:

class MyClass {…};

char buf=new char[Nsizeof(MyClass)+ sizeof(int) ] ; 或者char buf[N*sizeof(MyClass)+ sizeof(int) ];

  1. 对象的构造

MyClass * pClass=new(buf) MyClass;

  1. 对象的销毁

一旦这个对象使用完毕,你必须显式的调用类的析构函数进行销毁对象。但此时内存空间不会被释放,以便其他的对象的构造。

pClass->~MyClass();

  1. 内存的释放

如果缓冲区在堆中,那么调用delete[] buf;进行内存的释放;如果在栈中,那么在其作用域内有效,跳出作用域,内存自动释放。

注意:

1) 在C++标准中,对于placement operator new []有如下的说明: placement operator new[] needs implementation-defined amount of additional storage to save a size of array. 所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。

2) 使用方法第二步中的new才是placement new,其实是没有申请内存的,只是调用了构造函数,返回一个指向已经分配好的内存的一个指针,所以对象销毁的时候不需要调用delete释放空间,但必须调用析构函数销毁对象。
原文链接: https://www.cnblogs.com/pure/archive/2012/11/03/2752797.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/67997

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 下午1:07
下一篇 2023年2月9日 下午1:08

相关推荐