您的位置:首页 > 编程语言 > C语言/C++

C++11智能指针的常见误区 Top10

2017-05-13 16:41 260 查看

C++11智能指针的常见误区 Top10

我很喜欢C++11全新的智能指针,对于那些苦恼于自己管理内存的开发者来说,这是天大的好事。但是,在广泛使用C++11智能指针的两年里,因为使用方法不当而导致程序的效率下降甚至直接crash掉的事情发生过不知道多少此。为了给大家参考,举出下面的一些例子。

误区1:在使用unique_ptr就足够的情况下使用shared_ptr

我最近交接的一些业务中,甚至出现全部对象的生成都是使用shared_ptr,但是分析代码后发现,有大约9成被shared_ptr包裹的资源都不需要也没有被共用。

这样做,至少有以下两个问题:

1.程序的某一部分通过另一个共用指针改变额资源,但是当前指针并没有发现资源已经变更,将会导致不可预料的错误。即使另一个共用指针保持指向的资源不变,他可能超乎预料的长时间持有该资源的引用,即使当前指针离开作用域被销毁,该无用资源一直没能被释放;

2.shared_ptr比unique_ptr的生成时间更长,占用资源更多,运行效率更低。因为在shared_ptr的内部多出了引用计数和控制域,而且它们必须是线程安全的。

误区2:以为通过shared_ptr共用的资源/对象是线程安全的

虽然shared_ptr内部的引用计数和控制域都是线程安全的,但对于被shared_ptr管理的多线程共用资源依然需要基本的同步处理才能保证线程安全。

误区3:使用auto_ptr

auto_ptr的使用是危险的,当今已经不推荐使用了。使用该指针进行按值传参时会自动调用拷贝构造函数,这会导致整个资源的管理权的转移,这会导致原指针的地址失效,当对它进行取值操作时将直接crash。

因为unique_ptr和auto_ptr有相同的作用,而且前者在编写代码时就能自动检测出错误,所以建议将所有使用auto_ptr的地方都替换成unique_ptr。

int main()
{
auto_ptr<aircraft> myAutoPtr(new Aircraft("F-15"));
SetFlightCountWithAutoPtr(myAutoPtr); // Invokes the copy constructor for the auto_ptr
myAutoPtr->m_flyCount = 10; // CRASH !!!
}


误区4:shared_ptr初始化时并没有使用make_shared

shared_ptr<aircraft> pAircraft(new Aircraft("F-16")); // Two Dynamic Memory allocations - SLOW !!!


如果像上面那样初始化shared_ptr,会有两次动态的内存分配,一次是要作为被管理的新对象本身,另一次是shared_ptr构造函数生成的负责管理的指针对象。

但是C++编译器针对shared_ptr其实可以一次性生成一个较大的内存空间直接存放这两个对象,代码如下所示。

shared_ptr<aircraft> pAircraft = make_shared<aircraft>("F-16"); // Single allocation - FAST !


误区5:生成对象后没有马上将地址分配给shared_ptr

思考一下以下的例子。

int main()
{
Aircraft* myAircraft = new Aircraft("F-16");

shared_ptr<aircraft> pAircraft(myAircraft);
cout << pAircraft.use_count() << endl; // ref-count is 1

shared_ptr<aircraft> pAircraft2(myAircraft);
cout << pAircraft2.use_count() << endl; // ref-count is 1

return 0;
}


显然,当main函数运行结束时,这两个shared_ptr都会尝试释放同一个对象的内存(计数器变为0),这将会导致程序crash。因此就算你不使用make_shared初始化shared_ptr,也至少保证在生成对象后马上初始化shared_ptr,保证之后绝不用这个原始的指针生成另外的任何智能指针。

误区6:尝试获取shared_ptr所管理的原始指针

使用shared_ptr.get()可以获取shared_ptr管理的原始指针,但这应该极力避免。思考下一下代码。

void StartJob()
{
shared_ptr<aircraft> pAircraft(new Aircraft("F-16"));
Aircraft* myAircraft = pAircraft.get(); // returns the raw pointer
delete myAircraft;  // myAircraft is gone
}


crash原因跟误区5如出一辙

误区7:对于指针数组使用shared_ptr的时候不使用自定义的析构函数

思考一下下面的例子

void StartJob()
{
shared_ptr<aircraft> ppAircraft(new Aircraft[3]);
}


在这里,shared_ptr只指向了Aircraft[0],而数组的其他两个元素将不会被释放内存,从而造成内存泄露。应该参照下面的写法。

void StartJob()
{
shared_ptr<aircraft> ppAircraft(new Aircraft[3], [](Aircraft* p) {delete[] p; });
}


误区8:使用shared_ptr时要避免出现循环引用

思考下下面的例子

class Aircraft
{
private:
string m_model;
public:
int m_flyCount;
shared_ptr<Aircraft> myWingMan;
…


int main()
{
shared_ptr<aircraft> pMaverick = make_shared<aircraft>("Maverick: F-14");
shared_ptr<aircraft> pIceman = make_shared<aircraft>("Iceman: F-14");

pMaverick->myWingMan = pIceman; // So far so good - no cycles yet
pIceman->myWingMan = pMaverick; // now we got a cycle - neither maverick nor goose will ever be destroyed

return 0;
}


显然,当函数调用结束后,这两个新对象都不会被释放资源。因此当没有确实的必要获取对象的强引用(即保证对象一定要是存活的)的情况下,应该尽量使用weak_ptr,如下。

class Aircraft
{
private:
string m_model;
public:
int m_flyCount;
weak_ptr<Aircraft> myWingMan;
…


误区9:没有释放unique_ptr.release()返回的原始指针

因为release()方法并没有把unique_ptr所管理的对象释放掉,只是将unique_ptr和它管理的资源对象解除联系而已,所以需要你手动释放才行。

int main()
{
unique_ptr<aircraft> myAircraft = make_unique<aircraft>("F-22");
Aircraft* rawPtr = myAircraft.release();
return 0;
}


显然,myAircraft将一直存活下去。

误区10:调用weak_ptr.lock()时没有确认是否有效

在使用weak_ptr之前,调用它的lock()方法是有必要的,它实际上是获取并替换对应的对象的当前真实的值。但如果这时候这个对象已经被释放掉了,weak_ptr就会变成空指针,如果对它进行取值操作,结果可想而知。

class Aircraft
{
private:
string m_model;
public:
int m_flyCount;
weak_ptr<Aircraft> myWingMan;
…


int main()
{
shared_ptr<aircraft> pMaverick = make_shared<aircraft>("F-22");
shared_ptr<aircraft> pIceman = make_shared<aircraft>("F-14");

pMaverick->myWingMan = pIceman;
pIceman->m_flyCount = 17;

pIceman.reset(); // destroy the object managed by pIceman

cout << pMaverick->myWingMan.lock()->m_flyCount << endl; // ACCESS VIOLATION

return 0;
}


但也不能保证两次lock()之间对象一定不会被释放(第一次取出用来判空,第二次才用来真正访问),正确做法应该是以下这样的。

shared_ptr<aircraft> wingMan = pMaverick->myWingMan.lock();
if (wingMan)
{
cout << wingMan->m_flyCount << endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ top10