C++ 学习笔记——十、标准模板库
创始人
2025-05-29 20:31:07

目录:点我

一、智能指针模板类

智能指针是行为类似于指针的类对象,但这种对象还有其他功能,下面介绍三个可帮助管理动态内存分配的智能指针模板:

void remodel(string & str) {string * ps = new string(str);...str = *ps;return;
}

对于上面的函数,每当调用时,该函数都分配堆中的内存,但从不收回,导致内存泄漏,只需在 return 前添加 delete ps 即可释放内存。但是通常会忘记这一步,而且如果存在其他异常情况导致没有执行它,也会导致内存泄漏:

void remodel(string & str) {string * ps = new string(str);...if(weird_thing())  // 可能由于抛出异常导致未执行内存泄漏throw exception();str = *ps;delete ps;return;
}

遇到这种情况,就期望指针 ps 是一个对象,这样当它过期时就可以使用析构函数删除指向的内存,为此 C++ 提供了智能指针模板,其中 auto_ptr 是 C++ 98 提供的方案,C++ 11 已将其摒弃,并提供了另外两种:unique_ptr, shared_ptr ,它们都定义了类似指针的对象,可以将 new 获得的地址赋给它,并且当智能指针过期时,这些内存将自动被释放。

要创建智能指针对象,必须包含头文件 memory ,然后使用通常的模板语法来实例化所需类型的指针:

template class auto_ptr {
public:explicit auto_ptr(X* p = 0) throw();  // 必须包含这个构造函数...
}
auto_ptr pd(new double);  // double类型的智能指针
auto_ptr pd(new string);  // string类型
unique_ptr pdu(new double);  // 相似的用法
shared_ptr pss(new string);  // 相似的用法

从写 remodel 方法:

#include 
void remodel(std::string & str) {std::auto_ptr ps(new std::string(str));...if(weird_thing())  // 可能由于抛出异常导致未执行内存泄漏throw exception();str = *ps;return;
}

注意,智能指针模板位于名称空间 std 中,下面是一个使用例子:

class Report {
private:string str;
public:Report(const string s) : str(s) {cout << "created\n";}~Report() {cout << "deleted\n";}void comment() const {cout << str << endl;}
};
int main() {{auto_ptr ps(new Report("auto_ptr"));ps->comment();}{shared_ptr ps(new Report("shared_ptr"));ps->comment();}{unique_ptr ps(new Report("unique_ptr"));ps->comment();}
}
// outputs
created
auto_ptr
deleted
created
shared_ptr
deleted
created
unique_ptr
deleted

所有智能指针都有一个 explicit 构造函数,它将指针作为参数,因此不需要自动将指针转换为智能指针对象。

需要避免下面这种情况:

string v("example");
shared_ptr p(&v);

当 p 过期时,程序将把 delete 运算符用于非堆栈内存,这是错误的。

相同智能指针之间赋值也需要注意:

auto_ptr pa(new string("example")), pb;
pb = pa;

这样做会导致两个指针指向同一个内存,然后当它们相继过期时会对其进行两次 delete ,解决方法有多种:

  • 定义赋值运算符:使之成为深复制,即再创建一个副本,使两个指针指向不同的副本;
  • 建立所有权概念:对于特定的对象,只有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权。unique_ptr 和 auto_ptr 采用此策略,但前者更严格;
  • 创建智能更高的指针,根据引用特定对象的智能指针数,这称为引用计数(reference counting)。shared_ptr 采用此策略。

下面举一个不适用于 auto_ptr 的例子:

auto_ptr p1(new string("example")), p2;
p2 = p1;  // p1丧失所有权
cout << *p1;  // 发生错误:Segmentation fault (core dumped)

由于 p1 丧失所有权,因此变成一个空指针,此时访问 p1 发生错误;如果采用 shared_ptr 则能正常工作,因为二者都指向同一块区域;如果采用 unique_ptr 则会在编译过程中察觉错误,因此这样更安全。

再来看下一种情况:

unique_ptr demo(const string s) {unique_ptr tmp(new string(s));return tmp;
}
unique_ptr ps = demo("example");

此时由于 demo 函数返回一个临时的 unique_ptr ,然后 ps 接管了原本返回的 unique_ptr 所有的对象,而返回的 unique_ptr 被销毁,这没有问题,因为 ps 拥有了 string 对象的所有权。这样做的另一个好处是 demo 函数返回的临时 unique_ptr 很快被销毁,没有机会使用它访问无效数据,因此编译器允许这种赋值。

编译器是如何区分两种情况的呢?答案是判断右值:

unique_ptr p1(new string("example"));
unique_ptr p2;
p2 = p1;  // not allow
p2 = unique_ptr(new string("example"));  // allow

也就是说,如果源 unique_ptr 是一个临时右值,编译器允许赋值,反之不行。因此 unique_ptr 优于 auto_ptr ,因为后者将允许这两种赋值。如果一定要使用第一种方式赋值,可以采用 std::move() ,该函数类似于 demo ,将返回一个 unique_ptr 对象:

unique_ptr p1(new string("example"));
unique_ptr p2;
p2 = std::move(p1);  // allow

unique_ptr 优于 auto_ptr 的另一个原因是 unique_ptr 可以用于数组,而 auto_ptr 不能。因此 unique_ptr 可与 new [] 配套使用,而 auto_ptr 只能与 new 配套使用。

根据不同智能指针的特点,若有多个智能指针同时指向同一个对象,则使用 shared_ptr ,否则建议使用 unique_ptr 。

相关内容

热门资讯

重大通报“新版人皇究竟是不是有... 您好:新版人皇这款游戏可以开挂,确实是有挂的,需要软件加微信【5951795】,很多玩家在新版人皇这...
科普实测“炫龙 有没有透视 ”... 您好:炫龙这款游戏可以开挂,确实是有挂的,需要了解加客服微信【3398215】很多玩家在这款游戏中打...
今日重大发现{C7娱乐.到底有... 您好:C7娱乐这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
玩家必看“么么四川麻将到底是不... 您好:么么四川麻将这款游戏可以开挂,确实是有挂的,需要软件加微信【4770480】,很多玩家在么么四...
重大发现“茶虞姬 有没有挂可以... 您好:茶虞姬这款游戏可以开挂,确实是有挂的,需要软件加微信【5537821】很多玩家在这款游戏中打牌...