通常用容器保存指针比保存对象更好,而且大多数时候,保存智能指针比原生指针好。下面是一些原因:

  • 在容器中保存指针需要复制指针而不是它所指向的对象。复制指针通常比复制对象快。
  • 在容器中保存指针可以得到多态性。存放元素基类指针的容器也可以保存其派生类型的指针。当要处理有共同基类的任意对象序列时,这种功能是非常有用的。应用这一特性的一个常见示例是展示一个含有直线、曲线和几何形状的对象序列。
  • 对指针容器的内容进行排序的速度要比对对象排序快;因为只需要移动指针,不需要移动对象。
  • 保存智能指针要比保存原生指针安全,因为在对象不再被引用时,自由存储区的对象会被自动删除。这样就不会产生内存泄漏。不指向任何对象的指针默认为 nullptr。

如你所知,主要有两种类型的智能指针:unique_ptr<T> 和 shared_ptr<T>,其中 unique_ptr<T> 独占它所指向对象的所有权,而 shared_ptr<T> 允许多个指针指向同一个对象。还有weak_ptr<T> 类型,它是一类从 shared_ptr<T> 生成的智能指针,可以避免使用 shared_ptrs<T> 带来的循环引用问题。unique_ptr<T> 类型的指针可以通过移动的方式保存到容器中。例如,下面的代码可以通过编译:

std::vector<std::unique_ptr<std::string>> words;
words.push_back(std::make_unique<std::string>("one"));
words.push_back(std::make_unique<std::string>("two"));

vector 保存了 unique_ptr<string> 类型的智能指针。make_unique<T>() 函数可以生成对象和智能指针,并且返回后者。因为返回结果是一个临时 unique_ptr<string> 对象,这里调用一个有右值引用参数的 push_back() 函数,因此不需要拷贝对象。另一种添加 unique_ptr 对象的方法是,先创建一个局部变量 unique_ptr ,然后使用 std::move() 将它移到容器中。然而,后面任何关于拷贝容器元素的操作都会失败,因为只能有一个 unique_ptr 对象。如果想能够复制元素,需要使用 shared_ptr 对象;否则就使用 unique_ptr 对象。

在序列容器中保存指针

下面首先解释一些在容器中使用原生指针会碰到的问题,然后再使用智能指针(这是推荐的使用方式)。下面是一段代码,用来从标准输入流读取单词,然后将指向自由存储区的字符串对象的指针保存到 vector 容器中:

std::vector<std::string*> words;
std::string word;
std::cout << "Enter words separated by spaces, enter Ctrl+Z on a separate line to end: \n";
while (true)
{
if ((std::cin >> word).eof())
{
std::cin. clear();
break;
}words.push_back(new std::string {word});// Create object and store its address
}

push_back() 的参数表达式在自由存储区生成了一个字符串对象

for (auto iter = std::begin(words);iter != std::end(words); ++iter)
std::cout << **iter <<" ";
std::cout << std::endl;

,因此 push_back() 的参数是一个对象的地址。可以按如下方式输出 words 中的内容:

for (auto& w : words)
std: : cout << w <<" ";
std::cout << std::endl;

如果想使用迭代器来访问容器中的元素,输出字符串的代码可以这样写:

for (auto iter = std::begin(words);iter != std::end(words); ++iter)std::cout << **iter <<" ";
std::cout << std::endl;

iter 是一个迭代器,必须通过解引用来访问它所指向的元素。这里,容器的元素也是指针,因此必须解引用来获取 string 对象。因此表达式为:**iter。注意,在删除元素时,需要先释放它所指向的内存。如果不这样做,在删除指针后,就无法释放它所指向的内存,除非保存了指针的副本。这是容器中的原生指针常见的内存泄漏来源。下面演示它如何在 words 中发生:

for (auto iter = std::begin(words);iter != std::end(words);)
{
if (**iter == "one")
words.erase (iter); // Memory leak!
else
++iter;
}

这里删除了一个指针,但它所指向的内存仍然存在。无论什么时候删除一个是原生指针的元素,都需要首先释放它所指向的内存:

for (auto iter = std::begin(words); iter != std::end(words);)
{
if (**iter == "one")
{
delete *iter;//Release the memory...
words.erase (iter); //... then delete the pointer
}
else
++iter;
}

在离开 vector 的使用范围之前,记住要删除自由存储区的 string 对象。可以按如下方式来实现:

for (auto& w : words)
delete w; // Delete the string pointed to
words.clear(); // Delete all the elements from the vector

用索引来访问指针,这样就可以使用 delete 运算符删除 string 对象。当循环结束时,vector 中的所有指针元素都会失效,因此不要让 vector 处于这种状态。调用 dear() 移除所有元素,这样 size() 会返回 0。当然,也可以像下面这样使用迭代器:

for (auto iter = std::begin(words);iter != std::end(words); ++iter)
delete *iter;

如果保存了智能指针,就不用担心要去释放自由存储区的内存。智能指针会做这些事情。下面是一个读入字符串,然后把 shared_ptr<string> 保存到 vector 中的代码片段:

std::vector<std::shared_ptr<std::string>> words; std::string word;
std::cout << "Enter words separated by spaces, enter Ctrl+Z on a separate line to end:\n";
while (true)
{
if ((std::cin >> word).eof())
{
std::cin. clear ();
break;
}
words.push_back(std::make_shared<string>(word)); // Create smart pointer to string & store it
}

这和使用原生指针的版本没有什么不同。vector 模板现在的类型参数是 std::shared_ptr<std::string>,push_back() 的参数会调用 make_shared(),在自由存储区生成 string 对象和一个指向它的智能指针。因为智能指针由参数表达式生成,这里会调用一个右值引用参数版的 push_back() 来将指针移到容器中。

模板类型参数可能有些冗长,但是可以使用 using 来简化代码。例如:

using PString = std::shared_ptr<std::string>;

使用 using 后,可以这样定义:

std::vector<PString> words;

可以通过智能指针元素来访问字符串,这和使用原生指针相同。前面那些输出 words 内容的代码片段都可以使用智能指针。当然,不需要删除自由存储区的 string 对象;因为智能指针会做这些事情。执行 words.clear() 会移除全部的元素,因此会调用智能指针的析构函数;这也会导致智能指针释放它们所指向对象的内存。

为了阻止 vector 太频繁地分配额外内存,可以先创建 vector,然后调用 reserve() 来分配一定数量的初始内存。例如:

std::vector<std::shared_ptr<std::>>words;
words.reserve(100); // Space for 100 smart pointers

这样生成 vector 比指定元素个数来生成要好,因为每一个元素都是通过调用 shared_ptr<string> 构造函数生成的。不这样做也不是什么大问题,但会产生一些不必要的额外开销,即使开销很小。通常,每个智能指针所需要的空间远小于它们所指向对象需要的空间,因此可以大方地使用 reserve() 来分配空间。

可以在外面使用保存的 shared_ptr<T> 对象的副本。如果不需要这种功能,应该使用 unique_ptr<T> 对象。下面展示如何在 words 中这样使用:

std::vector<std::unique_ptr<std::string>>words;
std::string word;
std::cout << "Enter words separated by spaces, enter Ctrl+Z on a separate line to end:\n";
while (true)
{
if ((std::cin >> word).eof())
{
std::cin.clear();
break;
}
words.push_back(std::make_unique<string>(word));
//Create smart pointer to string & store it
}

在上面的代码中,用 unique 代替 shared 是没有差别的。

下面是一个完整的测试例子:

void SmartPointerFun()
{cout << "vector存放unique_ptr指针" << endl;vector<unique_ptr<string>>words;words.push_back(make_unique<string>("one"));words.push_back(make_unique<string>("two"));words.push_back(make_unique<string>("three"));unique_ptr<string>one(new string("four"));        //已经存在的unique_ptr不能再被放置到vector中,无法进行复制构造和赋值操作,该指针只能存在一个,使用move可以将其赋值给另一个指针对象,或者存储到vector备份//使用move将指针转移words.push_back(move(one));cout << "vector内容显示:";for (auto &iter:words){cout << *iter<<" ";}cout << endl;cout << "vector存放shared_ptr" << endl;vector<shared_ptr<string>>myvector;myvector.push_back(make_shared<string>("one"));myvector.push_back(make_shared<string>("two"));shared_ptr<string>two(new string("three"));      //已经存在的shared_ptr可以进行多个备份,支持复制构造和赋值操作,myvector.push_back(two);cout << "vector内容显示:";for (auto &iter : myvector){cout << *iter << " ";}cout << endl;}int main()
{SmartPointerFun();system("pause");
}

C++序列容器存储智能指针相关推荐

  1. c++学习:多线程;顺序容器;智能指针

    多线程的创建 创建线程比较简单,C++提供头文件thread,使用std的thread实例化一个线程对象创建. std::thread 在 #include 头文件中声明,因此使用 std::thre ...

  2. VTK修炼之道80:VTK开发基础_智能指针与引用计数

    1.引用计数 VTK经过多年的开发与维护,已经形成了一套稳定的框架和开发规则.因此,了解这些规则和框架是定制VTK类的基础,这其中用到了大量面向对象的设计模式,例如对象工程模式.观察者/命令模式:还有 ...

  3. 『C++11』智能指针 匠心之作

    优缺点: 智能指针由原始指针的封装,优点是可以自动分配内存,不用担心内存泄漏问题. 用于解决独占/共享所有权指针的释放,传输等问题. 但是没有原始指针方便. 目录 一. unique_ptr 独占指针 ...

  4. 重新学习c++--理解引用、智能指针、虚函数、模板、容器

    最近几年用c和python比较多,上次用c++写程序已经是几年前的事情了.温故而知新,是时候重新学习下c++了. C++可以分成四大块去理解: C c++仍以C为基础,区块blocks.语句state ...

  5. 智能指针可以放到容器中么_Rust语言入门教程 智能指针篇

    指针 (pointer)是一个包含内存地址的变量的通用概念.这个地址引用,或 "指向"(points at)一些其他数据.Rust 中最常见的指针是第四章介绍的 引用(refere ...

  6. boost any 实现万能容器_全面剖析 C++ Boost 智能指针!| CSDN 博文精选

    作者 | .NY&XX 责编 | 屠敏 出品 | CSDN 博客 为什么要使用智能指针 C++没有提供类似JAVA的垃圾回收机制,因此Boost可以通过智能指针来管理内存避免一些问题.C++继 ...

  7. C++专题:异常处理与转换函数,智能指针,STL模板

    目录 异常处理 转换函数 智能指针 STL标准模板库 异常处理 什么是异常? 程序中常见的错误分为两大类:编译时错误和运行时错误.编译时的错误主要是语法错误,如关键字拼写错误.语句末尾缺分号.括号不匹 ...

  8. 五点讲述C++智能指针的点点滴滴

    (在学习C/C++或者想要学习C/C++可以加我们的学习交流QQ群:712263501群内有相关学习资料) 0.摘要 本文先讲了智能指针存在之前C++面临的窘境,并顺理成章地引出利用RAII技术封装普 ...

  9. 【C++】智能指针(一)入门

    1. 智能指针背后的设计思想 智能指针背后的思想是RAII,参见博客[C++]零散知识 我们先来看一个简单的例子: void remodel(std::string & str) {std:: ...

  10. C++知识点36——使用智能指针的注意事项(下)

    四.智能指针与容器 当把shared_ptr对象放入一个容器中时,会调用shared_ptr的拷贝构造函数并且引用计数+1 int main(int argc, char const *argv[]) ...

最新文章

  1. ajax: PopupControlExtender使用
  2. 经典日剧、电影、动漫
  3. c语言中调整颜色的函数_C语言中的输入输出函数
  4. XCode环境变量及路径设置
  5. java中的正则表达式捕获组与引用的概念
  6. 深度学习之Windows下安装faster-rcnn
  7. C#新手该如何规划学习【学习路线指南】
  8. Unity客户端开发优化要点
  9. 神结合!一招玩转K8s和微服务治理
  10. mysql数据库表的类型介绍,mysql数据库表的类型介绍
  11. DataBinder.Eval数据绑定中的使用
  12. 【clickhouse】clickhouse表引擎之ReplacingMergeTree
  13. Selenium自动化测试-8.iframe处理
  14. Ubuntu16.04上使用Anaconda3的Python3.6的pip安装UWSGI报错解决办法
  15. 物业计算机管理系统论文,小区物业管理系统设计毕业论文
  16. bam文件flag的含义
  17. 安装AD域时先决条件不通过
  18. 根据简化真值表绘制电路
  19. linux系统下grub.cfg详解和实例操作
  20. 学英语《每日一歌》之see you again(速度与激情7主题曲)

热门文章

  1. Java与MySQL连接错误_mysql连接错误
  2. python以追加方式打开文件 线程安全吗_Python多线程同步---文件读写控制方法
  3. 华为新系统鸿蒙,爆料|疑似华为新MatePad Pro包装盒曝光:搭载鸿蒙OS
  4. git克隆权限_git clone权限被拒绝
  5. linux下安卓刷机,linux下安卓刷机脚本
  6. 华硕开机画面修改_电脑开机密码忘记,进不了系统,一招教你轻松解决!
  7. python的浮点数_python 浮点数 转 整数python函数每日一讲 - all()
  8. 删改数据如何避免锁表?等等,啥是锁呀
  9. php访问对象的成员,如何使用PHP从JSON访问另一个对象内的类的成员
  10. apache ii评分怎么评_APACHEⅡ评分系统