动态内存的基本功能和使用

  • 基本知识
    • 动态内存
    • new和delete
    • shared_ptr
    • StrBlob类与StrBlobPtr类
  • 示例代码
    • 程序1
    • 程序2
    • 程序3

本文是本人大一期间在校学习C++课程时所撰写的实验报告的摘录,由于刚上大学,刚接触计算机编程方面的相关知识,故可能会有很多不足甚至错误的地方,还请各位看官指出及纠正。
本文所涉及到的“教材”指:
电子工业出版社《C++ Primary中文版(第5版)》
如需转载或引用请标明出处。

本文涉及到的三个程序通过自定义了一个结构体Foo、类型StrBlob、指向其的类型StrBlobPtr以及定义其他一些操作这些类或结构体的函数,来演示了如何使用动态内存及其特点,并展示了shared_ptr的用法和特点。下面是一些相关的知识点。

基本知识

动态内存

除了自动和static对象外,C++还支持动态分配对象。动态分配的对象的生存期与它们在哪里建创是无关的,只有当显示地被释放时,这些对象才会销毁。也就是说,动态对象的生存期由程序来控制,当动态对象不再使用时,我们的代码必须显示地销毁它们。

new和delete

在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化;delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。

然而想通过使用这两个关键字管理动态内存很容易出问题,因为我们很容易就忘了释放内存,这种情况下就会产生内存泄漏;有时在尚有指针应用内存的情况下我们就释放了它,这时就会产生引用非法内存的指针。

为了更安全地使用动态内存,新的标准库提供了几种智能指针类型来管理动态对象。下面主要介绍shared_ptr。

shared_ptr

shared_ptr类似于vector,智能指针也是模板,因此建创智能指针时须告知编译器该指针可以指向的类型。例如,我们可以像这样去建创智能指针:

shared_ptr<string> p1;             //p1可以指向string
shared_ptr<vector<string>> p2;      //p2可以指向由string组成的vector

智能指针的用法和普通的指针类似,都可以通过解引用智能指针返回它指向的目标,都可以通过->运算符返回它指向目标的成员等。

shared_ptr最大的特点是,可以认为每个shared_ptr都有一个关联的计数器,通常也称其为应用计数。无论何时我们拷贝一个shared_ptr时,计数器都会递增。

例如,当引用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数以及作为函数的返回值时,它关联的计数器就会递增。当我们给shared_ptr赋予一个新值或是shared_ptr被销毁(例如一个局部的shared_ptr离开其作用域)时,计数器会递减。一旦一个shared_ptr的计数器变为0,它就会自动销毁自己所管理的对象,并释放它们占用的内存。而这一特性使得正确、安全地使用动态内存变得非常容易。

具体例子详见代码注释部分。

StrBlob类与StrBlobPtr类

这两个类是自定义的类,用于演示如何使用动态内存。其中StrBlob类是对象本身,StrBlobPtr类是用于管理对象的类似于指针的类型。其中的一些操作函数的实现方法详见代码注释部分。

示例代码

程序1

① Foo.h

#ifndef FOO_H  //如果没有定义FOO_H,则进行以下定义
#define FOO_H#include <iostream>typedef int T;                //定义新的int类型T
struct Foo {                //默认情况下Foo的成员是公共的Foo(T t): val(t) { }    //重载,使Foo(t)相当于令Foo成员val的值为tT val;                  //Foo只有一个成员val
};//定义输出函数,接受一个输出流和一个Foo类型数据的引用为实参
std::ostream& print(std::ostream &os, const Foo &f)
{os << f.val;  //通过输出流os输出f的成员valreturn os;     //返回os
}#endif  //定义结束

② allocPtr.cpp

#include <vector>
using std::vector;#include <string>
using std::string;#include <iostream>
using std::istream; using std::ostream;
using std::cin; using std::cout; using std::endl;                                                                                                                               //包含有Foo类型相关信息的头文件
#include "Foo.h"//factory函数返回一个指向动态分配内存对象的指针,所指类型为Foo
//作用为生成一个动态分配内存的对象
Foo* factory(T arg)
{//恰当地处理参数arg//通过new关键字,动态分配一块内存大小为Foo的大小的对象,用arg为参数初始化该对象//同时调用方负责释放此内存return new Foo(arg);
}//use_factory函数返回一个指向动态分配内存对象的指针,所指类型为Foo
//作用为对其中的数据进行操作
Foo* use_factory(T arg)
{Foo *p = factory(arg);            //通过调用factory函数,动态分配Foo所需的空间,用指针p指向print(cout, *p);               //用自定义的输出函数将p指向的对象的内容(val)输出到cout cout << endl;                 //结束一行//返回p以便被调用方使用return p;                        //调用方必须记得释放动态分配的内存
} int main()
{T arg;while (cin >> arg) {           //有正确输入使执行循环Foo *p = use_factory(arg);  //用指针p指向use_factory函数的返回值delete p;                 //使用之后释放p所指向的动态内存}system("pause");return 0;
}

运行结果:
依次输入11 22 33 55 88 -99 00 z[enter],窗口显示:

程序2

① Foo.h
同上。
② allocSP.cpp

#include <vector>
using std::vector;                                                                                                                      #include <string>
using std::string;#include <memory>
using std::shared_ptr;#include <iostream>
using std::istream; using std::ostream;
using std::cin; using std::cout; using std::endl;//包含有Foo类型相关信息的头文件
#include "Foo.h"//factory返回一个指向动态分配内存对象的shared_ptr指针,所指类型为Foo
shared_ptr<Foo> factory(T arg)
{//恰当地处理参数arg//shared_ptr会智能地管理动态对象,确保在恰当的时候释放内存//通过new关键字,动态分配一块内存大小为Foo的大小的对象,用arg为参数初始化该对象//一个shared_ptr指向该对象,并作为函数返回值返回                                                                                               return shared_ptr<Foo>(new Foo(arg));
}//use_factory函数返回一个指向动态分配内存对象的share_ptr指针,所指类型为Foo
//作用为对其中的数据进行操作
shared_ptr<Foo> use_factory(T arg)
{shared_ptr<Foo> p = factory(arg);         //通过调用factory函数,动态分配Foo所需的空间,用share_ptr指针p指向,其计数器加一print(cout, *p);                        //用自定义的输出函数将p指向的对象的内容(val)输出到cout cout << endl;                             //结束一行//返回p以便被调用方使用return p;                                //当返回p时,share_ptr内置的计数器会自动增加1
}                                             //离开作用域时计数器自动减一,当计数为零时自动释放所指向的内存int main()
{T arg;while (cin >> arg)                       //有正确输入使执行循环use_factory(arg);                   //操作数据,且自动完成释放内存的任务system("pause");                                                                                                                                                       return 0;
}

运行结果同程序1

程序3

① StrBlob.h

//如果没有定义STRBLOB_H,则进行以下定义
#ifndef STRBLOB_H
#define STRBLOB_H
#include <vector>
#include <string>
#include <memory>                                                 //使用shared_ptr等
#include <stdexcept>                                              //使用一些异常类class StrBlobPtr;                                                  //该声明需要在StrBlob中进行友元声明class StrBlob {friend class StrBlobPtr;                                       //声明StrBlobPtr为友元类
public:                                                             //公开成员typedef std::vector<std::string>::size_type size_type;          //定义简写的size_typeStrBlob() : data(new std::vector<std::string>()) { }          //重载构造,使StrBlob()相当于用使用new动态分配内存大小为vector<std::string>构造的data                                                                                                                                      StrBlob(const std::string*, const std::string*);                //之前的C++11没有initializer_list,我们将定义一个构造函数//它的指针指向一个数组//定义关于size的操作size_type size() const { return data->size(); }                    //size函数返回成员data的大小bool empty() const { return data->empty(); }                  //empty函数判断data是否为空//定义增加、删除元素的操作void push_back(const std::string &t) { data->push_back(t); }    //在成员data中添加类型为string的元素tvoid pop_back();//定义元素访问的操作std::string& front();                                           //返回data的头元素std::string& back();                                            //返回data的尾元素//下面是关于StrBlobPtr的接口StrBlobPtr begin();StrBlobPtr end();
private:                                                            //私有成员
std::shared_ptr<std::vector<std::string> > data;                //data为指向存放string的vector的shared_ptr指针                                                                                                                                                                 void check(size_type i, const std::string &msg) const;            //检查函数,如果data[i]不存在则抛出异常msg
};//使用内联关键字重载构造函数
inline StrBlob::StrBlob(const std::string *beg, const std::string *end): data(new std::vector<std::string>(beg, end)) { }//StrBlobPtr在尝试访问不存在的元素时抛出异常
class StrBlobPtr {friend bool eq(const StrBlobPtr&, const StrBlobPtr&);         //声明友元函数eq
public:                                                             //公开成员StrBlobPtr(): curr(0) { }                                     //重载构造,使StrBlobPtr()相当于curr(0)StrBlobPtr(StrBlob &a, size_t sz = 0): wptr(a.data), curr(sz) { }  //同为重载构造std::string& deref() const;                                        //成员函数derefStrBlobPtr& incr();                                              //成员函数incr()StrBlobPtr& decr();                                             //成员函数decr()
private://如果检查成功的话,check函数返回一个指向该vector的shared_ptr指针std::shared_ptr<std::vector<std::string>> check(std::size_t, const std::string&) const;//成员wptr的类型为指向vector<string>的weak_ptr指针,意味着其指向的对象可能被释放                                                                                                                                                               std::weak_ptr<std::vector<std::string> > wptr;std::size_t curr;                                             //成员curr用于指示当前位置
};inline std::string& StrBlobPtr::deref() const
{//检查该位置是否超出vector的范围std::shared_ptr<std::vector<std::string>> p = check(curr, "dereference past end"); return (*p)[curr];                                               //(*p)是该指针指向的vector
}inline std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string &msg) const
{//检查该向量vector是否存在std::shared_ptr<std::vector<std::string> > ret = wptr.lock();   //如果不存在,则抛出异常if (!ret)throw std::runtime_error("unbound StrBlobPtr");//如果超出范围,则抛出另一个异常                                                                                                                                              if (i >= ret->size()) throw std::out_of_range(msg);return ret;                                                        //否则返回一个指向vector的shared_ptr指针
}//返回对递增对象的引用
inline StrBlobPtr& StrBlobPtr::incr()
{//如果curr早就超过了容器的范围,则不能递增check(curr, "increment past end of StrBlobPtr");              //检查curr的值是否合法++curr;                                                         //增加currreturn *this;                                                   //返回当前对象
}inline StrBlobPtr& StrBlobPtr::decr()
{//如果curr的值为0,则递减它会产生一个无效的目标--curr;                                                          //向前移动一个元素check(-1, "decrement past begin of StrBlobPtr");                //如果curr的值为-1, 则抛出异常
return *this;                                                   //返回当前对象
}//返回StrBlob中的头元素和尾元素
inline StrBlobPtr StrBlob::begin()
{return StrBlobPtr(*this);                                      //返回头元素
}inline StrBlobPtr StrBlob::end()
{StrBlobPtr ret = StrBlobPtr(*this, data->size());              //返回尾元素return ret;
}//关于StrBlobPtr中相同元素的操作
inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{std::shared_ptr<std::vector<std::string>> l = lhs.wptr.lock(), r = rhs.wptr.lock();//如果这两个vector是同一个的话if (l == r) //如果它们同时指向NULL或同时指向同一个元素的话,说明它们是相等的                         return (!r || lhs.curr == rhs.curr);elsereturn false;                                               //如果它们指向不同的vector,说明它们不相等
}//上面函数的相反
inline bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs)
{return !eq(lhs, rhs);
}
#endif                                                              //结束定义

② useBlob.cpp

#include <iostream>
using std::cout; using std::endl;#include <string>
using std::string;//包含有StrBolb类型相关信息的头文件
#include "StrBlob.h"int main()
{StrBlob b1;                                                            //定义一个StrBlob类型b1{//划分出一个新的块string temp[] = { "a", "an", "the" };                            //定义临时的string数组temp,用于存储数据StrBlob b2(temp, temp + sizeof(temp) / sizeof(*temp));        //在块中定义另一个StrBlob类型b2,用temp中的元素进行初始化,b2的计数器加一b1 = b2;                                                        //将b2赋值给b1,递增b2内置的计数器,此时b2计数为2b2.push_back("about");                                            //向b2中添加元素"about"cout << b2.size() << endl;                                       //输出b2的大小}   //离开b2的作用域,b2内置的计数器减一,此时b2计数为1,不为0,因此对象没有被销毁cout << b1.size() << endl;                                         //输出b1的大小,若一样,则说明b1和b2是同一个vector//当StrBlobPtr类型的指针it不指向b1的尾元素时执行循环for (StrBlobPtr it = b1.begin(); neq(it, b1.end()); it.incr())cout << it.deref() << endl;                                      //输出it指向的元素,然后递增itsystem("pause");return 0;
}

运行结果:

说明b1和b2指向的是同一个vector。

动态内存的基本功能和使用相关推荐

  1. 内存区划分;内存分配;堆、栈概念分析;动态内存管理数据结构及程序样例;核心态与用户态...

    一. 在c中分为这几个存储区1.栈 - 由编译器自动分配释放 2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初 ...

  2. 释放变量所指向的内存_C++动态内存分配(学习笔记:第6章 15)

    动态内存分配[1] 动态申请内存操作符 new new 类型名T(初始化参数列表) 功能: 在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值. 结果值: 成功:T类型的指针,指向 ...

  3. C++中的动态内存分配

    1.Cpp中的内存分配 了解动态内存在C++中是如何工作的是成为一名合格的C++程序员必不可少的.C++程序中的内存分为两个部分: 栈:在函数内部声明的所有变量都将占用栈内存. 堆:这是程序中未使用的 ...

  4. SCVMM 2008 R2 SP1 快速修改动态内存

    VMM 2008 R2 SP1 RC版已经发布,迫不及待下载下来部署并测试使用,我们知道其最有价值的功能在于动态内存功能.但这里在配置中遇到了个大难题,当我有一定数量的虚拟机模板时,手动去配置修改动态 ...

  5. C语言动态内存相关函数

    C语言动态内存管理函数有4个,分别为malloc,realloc,calloc和free.malloc函数分配一块堆内存:calloc是malloc的变种,功能相同,有细小的差别:realloc修改原 ...

  6. C语言之动态内存管理与动态内存函数

    文章目录 一.为什么存在动态内存分配? 二.动态内存函数的介绍 1.malloc和free 2.calloc函数 3.realloc函数 一.为什么存在动态内存分配? 学习动态内存的管理方法之前,我们 ...

  7. 从更底层研究C\C++动态内存分配

    2019独角兽企业重金招聘Python工程师标准>>> 以前在学C++ 的时候,一直不懂:动态内存分配的本质,或者更加深入到底层的意义.虽然说,动态内存分配就是,随机在内存中分配一个 ...

  8. c语言 动态内存相关函数

    动态内存相关函数 malloc calloc realloc free malloc 原型:extern void *malloc(unsigned int num_bytes); 用法:#inclu ...

  9. 深入浅出之动态内存(new,malloc深度分析)

    void GetMemory(char *p) { p = (char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(s ...

最新文章

  1. UVA10780幂和阶乘
  2. 【计算机网络】网络安全 : 总结 ( 网络攻击类型 | 网络安全指标 | 数据加密模型 | 对称密钥密码体质 | 公钥密码体质 | 数字签名 | 报文鉴别 | 实体鉴别 | 各层安全 ) ★
  3. ABB RAPID 程序 WorldZone 归纳
  4. strip string java_Java StringUtils.strip方法代码示例
  5. React Native悬浮效果组件
  6. java飞机大战子弹怎么修改_java改版飞机大战源码
  7. lesson 4 Show Messages in Messagebox
  8. day12 python学习随笔 中
  9. 牛客练习赛43F Tachibana Kanade Loves Game
  10. Nordic Blue Tooth
  11. 机器人学中的状态估计 中文版_机器人学——学习笔记18(Minpulator Traj Planning Example)...
  12. 在手机上实现实时的单目3D重建
  13. 基于新标注模式的实体和关系联合抽取方法(Joint Extraction of Entities and Relations Based on a Novel Tagging Scheme)
  14. PreferenceScreen的应用
  15. 达观数据:中文和英文语言差异和计算机处理的区别
  16. 项目(1)——文件压缩
  17. selenium网页截图总结
  18. 荷尔蒙飘香的套套市场,谁才是王者?
  19. 猎企如何制定合理的数据战略?(附数据迁移三大注意点)深度干货
  20. MarkDown CheatSheet 速查表

热门文章

  1. 2017.0612.《计算机组成原理》总线结构
  2. 给JBoss种蛊分析
  3. 操作系统实验报告18:硬盘柱面访问调度算法
  4. xss权限维持(小技巧)
  5. mysql buffer_mysql read_buffer_size 设置多少合适
  6. SwiftUI之深入解析布局如何自定义AlignmentGuides
  7. iOS实现“下雨下雪”动画效果和“烟花”动画效果
  8. 在OSI参考模型中,当两台计算机进行文件传输时,为防止中间出现网络故障而重传整个文件的情况,可通过在文件中插入同步点来解决,这个动作发生在( )
  9. Django MPTT —— 概述
  10. 【机器视觉】Qt联合Halcon编程之显示图片