Big Three & operator new/delete[GeekBand]

1.Big Three

当我们定义一个类以后有,如果没实现这拷贝构造函数、拷贝复制函数和析构函数,编译器会自动为我们生成这3个函数。但是,编译器自动生成的这拷贝构造函数和拷贝复制函数只进行简单的内存复制。如果我们定义的类的数据成员包括指针的话,使用编译器自动生成的这套函数就会有问题(复制和拷贝的对象实际都指向同一个地方,这并不是我们想要的),因此如果类中包含带指针的数据成员则一定要实现这三个函数。

1.1拷贝构造函数(copy ctor)

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数是拷贝构造函数。拷贝构造函数被调用的情况包括:

  • a)使用=定义变量,如果A b; A a = b;
  • b)将一个对象作为实参传递给非引用类型的形参;
  • c)从一个返回类型为非引用的类型的函数返回一个对象
  • d)用花括号初始化一个数组的成员或者聚合类的成员

1.2拷贝赋值函数(copy op=)

拷贝赋值函数,其实就是对赋值操作符(=)进行重载。一般,拷贝赋值函数接受自身类类型的引用作为参数并返回自身类类型的引用。如:A& operator=(const A& rhs);

1.3析构函数(dtor)

析构函数,释放对象使用的资源,并销毁对象的非static数据成员。析构函数被调用的情况:

  • a)离开作用域
  • b)对象被销毁
  • c)容器被销毁时,其元素也被销毁
  • d)动态分配的对象,调用对应的delete
  • e)对于临时对象, 当创建他的表达式结束时。

2.stack & heap

2.1 stack & heap 定义

  • a)stack
    所谓的stack,是存在于某作用域(scope)的一块内存空间。
  • b)heap
    Heap,或谓system heap,是由操作系统提供的一块global内存空间,程序可动态分配从中获取若干区块。

2.2 new & delete

2.2.1 new

c++可以使用new来分配内存空间。new的用法有三种,包括:

  • a)plain new
    plain new其实就是我们最常使用的方式,new分配内存失败时并不是返回NULL,而是抛出异常std::bad_alloc.std::bad_alloc和new的声明在头文件new中。
    函数声明:
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)  __attribute__((__externally_visible__));
void* operator new[](std::size_t) _GLIBCXX_THROW (std::bad_alloc)  __attribute__((__externally_visible__));

用法:

#include <new>
std::size_t sz = 100;
char *parr = NULL;
char *p = NULL;
try{
    parr = new char[sz];
    p = new char;
}catch(std::bad_alloc& ex){
    std::cout << ex.what() << std::endl;
}
  • b)nothrow new
    nothrow new和plain new唯一的不同在于,new分配失败后并不抛出异常,而是返回NULL.同样nothrow new也在头文件<new>中声明。函数声明:
void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT  __attribute__((__externally_visible__));
void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT  __attribute__((__externally_visible__));

用法:

#include <new>
#include <iostream>
std::size_t sz = 100;
char *parr = new(nothrow) char[sz];
if (!parr){
    std::cout << "new error" << std::endl;
}
char *p = new(nothrow) char;
if (!p){
    std::cout << "new error" << std::endl;
}

说明:nothrow是头文件声明的对象,可以直接使用。

  • c)placement new

函数声明:

inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT;
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT;

用法:

void *buffer = new char[sz];
String *p = new(buffer) String[2];

按照标准库<new>中的分类,plain new和nothrow new是replaceable,用户可以通过重载操作符来实现自定义的内存管理,placement new是不可重载的操作。

  • plain new和nothrow new执行的动作如下:
    1.分配空间
    2.初始化对象
    比如说,我们执行Complex *p = new Complex(1, 2);等价于如下代码:
void *mem = operator new(sizeof(Complex));  //分配空间
pc = static_cast<Complex *>men; //类型转换
pc->Complex::Complex(1,2); //初始化对象
  • placement new执行的动作如下:
    1初始化对象
    operator new并没有进行内存空间的分配,只是返回之前分配的空间指针。在<new>头文件中,有一份placement new的默认实现。代码如下:
// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }
inline void* operator new[](std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT
{ return __p; }

2.2.2 delete

c++删除动态分配的Heap内存使用delete。delete的用法也和new一样存在三种且一一对应。
声明:

//plain delete
void operator delete(void*) ;
void operator delete[](void*) ;
//nothrow delete
void operator delete(void*, const std::nothrow_t&) ;
void operator delete[](void*, const std::nothrow_t&) ;
//placement delete
inline void operator delete  (void*, void*);
inline void operator delete[](void*, void*) ;

虽然delete依然有三类声明,但是只有plain delete可以通过delete-expression的方式使用。nothrow delete和placement delete只在相应的new-expression抛出异常时由系统自动调用。如果想手动使用nothrow delete和placement delete可以通过函数调用的方式。
nothrow delete和placement delete的函数使用方式:

operator delete(p, nothrow);
operator delete(buff, p);

2.3 array new与array delete探析

array new和array delete要配对使用,如果不配对使用可能出现内存泄漏。出现内存泄漏的原因并不是因为delete不能完全释放array new分配的内存,而是因为使用delete释放array new分配的内存时,编译器只对array new分配的数组的某一个对调用析构函数。这样就会导致数组中的其他对象因为没有调用析构函数而导致内存没有正确释放。
下面通过一个例子来说明这个问题。
代码:

void ArrayNewAndArrayDeleteTest()
{
    cout << "ArrayNewAndDelete:" << endl;
    String *p1 = new String[3];
    delete p1;
    cout << "ArrayNewAndArrayDelete:" << endl;
    String *p2 = new String[3];
    delete[] p2;
}

运行结果:

****begin run function ArrayNewAndArrayDeleteTest *****
ArrayNewAndDelete:
normal ctor:null string
normal ctor:null string
normal ctor:null string
dtor:
ArrayNewAndArrayDelete:
normal ctor:null string
normal ctor:null string
normal ctor:null string
dtor:
dtor:
dtor:
****end run function ArrayNewAndArrayDeleteTest ****

从结果可以看出,

  • 使用array new分配数组p1时调用了3次构造函数(3个normal ctor输出),使用delete释放内存时只调用了一次析构函数(只有一次dtor输出);
  • 使用array new分配数组p2时调用了3次构造函数(3个normal ctor输出),使用array delete释放内存时调用了3次析构函数(3个dtor输出)
    综上,array new配合delete,会导致析构函数只调用一次,从而导致内存泄漏。

3.其他

编译器按照数据成员的声明顺序初始化对象,而不是按照初始化列表声明的顺序初始化对象。构造函数的初始化列表最好和类的数据成员的声明顺序一致,且最好不要使用类的其他成员来初始化类的成员。

说明:
1.本文使用的String对象为侯老师上课所用String class,只是对函数的调用添加输出语句。
2.使用编译器为g++ 4.8.1
引用:
1.http://www.cnblogs.com/resound/archive/2011/10/27/2226472.html
2.GeekBand课件
3.cpp primer 5th edition
4.http://en.cppreference.com/w/cpp/memory/new/operator_delete
5.http://en.cppreference.com/w/cpp/memory/new/operator_new

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容