由容器PStash的使用者,负责清除容器中的所有指针。所以用户必须记住放到容器中的是什么类型,在取出时,把取出的void指针转换成对应的类型指针,然后 'delete 转换后的对象指针',才能在清除时调到对象的析构函数。

析构函数的作用: 确保对象的各部分被正确的清除,及做一些用户指定的其他清理工作。

1 头文件PStash.h

 #ifndef PSTASH_H
#define PSTASH_H class PStash
{
int capacity;
int next;
void** storage; //void* st[]; void** storage = st;可以看是一个'指向指针数组'首元素的指针
void inflate(int increase);
public:
PStash() : capacity(), next(), storage() {}
~PStash(); int add(void* element);
void* operator[](int index) const;
void* remove(int index);
int count() const { return next; }
};
#endif

2 PStash.cpp

 #include "PStash.h"
#include "../require.h"
#include <iostream>
#include <cstring> using namespace std; PStash::~PStash()
{
for (int i = ; i < next; i++)
{
//如果storage[i] == 0为false,即指针值storage[0,1,2,3,4]有不为0的,表明存在没有清除的元素
require(storage[i] == , "PStash not cleaned up");
} // void** st = new void*[capacity + increase];
// storage 是void类型指针的数组,即数组storage的元素是void型指针
delete []storage; //清除指针数组这个容器
storage = ;
cout << "after delete []storage;" << endl;
} int PStash::add(void* element)
{
const int ssize = ;
if (next >= capacity)
inflate(ssize);
cout << "storage[" << next << "] = " << element << endl;
storage[next++] = element;
return next - ;
} //重载[]运算符, intPStash[i] 返回值是void类型指针
void* PStash::operator [](int index) const
{
require(index >= , "PStash::operator[] index negative");
if (index >= next)
return ; return storage[index];
} //把栈中index索引处的元素(指针)清零
void* PStash::remove(int index)
{
void* v = operator[](index);
if (v != )
storage[index] = ;
return v;
} void PStash::inflate(int increase)
{
const int psz = sizeof(void*); //地址占4个字节 //创建了一个void*数组(void型指针的数组)
//该语句在堆上一口气创建capacity + increase个void指针
void** st = new void*[capacity + increase]; //new 一个包含 capacity + 10个void*元素的指针数组 memset(st, , (capacity + increase) * psz); //该指针数组st共占(capacity + increase) * psz个字节 //storage是void类型指针数组,所有拷贝的是数组中元素(指针),是地址拷贝,不存在清除对象问题
//拷贝完后,要清除这个废弃的指针数组
memcpy(st, storage, capacity * psz); //把从storage地址开始的capacity * psz字节内容拷贝到st地址空间 capacity += increase;
delete []storage;
storage = st;
}

3 测试文件PStashTest.cpp -- PStash使用者

 #include "PStash.h"
#include "../require.h"
#include <iostream>
#include <fstream>
#include <string>
#include "Book.h" using namespace std; int main()
{
{
PStash intStack;
for (int i = ; i < ; i++)
{
intStack.add(new int(i));
}
for (int k = ; k < ; k++ )
{
delete intStack.remove(k);
}
} cout << "--------------------------------" << endl; {
PStash strings; string* s1 = new string("hello");
string* s2 = new string("world"); cout << s1 << endl;
cout << s2 << endl; strings.add(s1);
strings.add(s2); delete (string*) strings.remove();
delete (string*) strings.remove();
} cout << "-----------------------------------" << endl; {
Book* b1 = new Book("算法精解", "Kyle Loudon", 56.2);
Book* b2 = new Book("Qt程序设计", "Pulat", 10.2); PStash books;
books.add(b1);
books.add(b2); cout << "book1: " << b1 << endl;
cout << "book2: " << b2 << endl;
delete (Book*) books.remove();
delete books.remove(); //books.remove(1)返回的是void型指针,所以此次的delete不会调用Book类的析构函数,而仅仅是释放了内容
//通常在析构函数中,会完成一些其他操作
} cout << "--------------- end ---------------" << endl;
return ;
};

运行结果:

运行过程分析:

附Book类定义

Book.h

 #ifndef BOOK_H
#define BOOK_H
#include <string> using std::string; class Book
{ string name;
string author;
double price; public:
Book();
Book(string name, string author, double price); //复制构造函数
Book(const Book& b); ~Book(); //把重载的<<运算符全局函数声明为友元
friend std::ostream& operator<<(std::ostream& os, const Book& b)
{
return os << "BookName: " << b.name << ", BookAuthor: " << b.author << ", BookPrice: " << b.price;
} //重载赋值运算符
Book& operator=(const Book& b);
};
#endif

Book.cpp

 #include "Book.h"
#include <iostream> using namespace std; Book::Book() : name("null"), author("null"), price()
{
cout << "invoke constructor Book() " << endl;
} Book::Book(string name, string author, double price) : name(name), author(author), price(price)
{
cout << "invoke constructor Book(string " << name << ", string " << author << ", double "<< price << ") " << endl;
} //复制构造函数
Book::Book(const Book& b) : name(b.name), author(b.author), price(b.price)
{
cout << "Book::Book(const Book& b)" << endl;
} Book::~Book()
{
cout << "~Book()" << endl;
cout << "free book: '" << name << "'" << endl;
} //重载赋值运算符
Book& Book::operator=(const Book& b)
{
cout << "Book::operator=(const Book&)" << endl;
name = b.name;
author = b.author;
price = b.price; return *this;
}

附, 模板化实现, 当模板化容器对象超出作用域时,能够负责清理容器中剩余的指针元素指向的对象

  --- 因为模板化容器知道容器中存放元素的类型 (PStash<Book>,在目标特化时,容器中元素的类型已限定)

1)模板定义文件TPStash.h

 #ifndef TPSTASH_H
#define TPSTASH_H #include "../require.h" template<class T, int incr = >
class PStash
{
int capacity;
int next;
T** storage;
void inflate(int increase = incr); public: PStash() : capacity(), next(), storage() {}
~PStash(); int add(T* element);
T* operator[](int index) const;
T* remove(int index);
T* pop();
int count() const { return next; }
}; //插入T型的指针元素element到容器,并返回插入位置索引
template<class T, int incr>
int PStash<T, incr>::add(T* element)
{
if (next >= capacity)
inflate(incr); storage[next++] = element;
return next - ;
} //重载运算符[]
// T* ele = pstash[2]
// 入参: int, 返回值类型: T*, 该容器插入和取出的都是T类型的指针
template<class T, int incr>
T* PStash<T, incr>::operator[](int index) const
{
//若index >= 0为false,则向stderr打印错误提示信息"PStash::operator[] index negative",并终止程序的执行
require(index >= , "PStash::operator[] index negative"); if (index >= next)
return ; require(storage[index] != , "PStash::operator[] returned null pointer"); return storage[index];
} template<class T, int incr>
T* PStash<T, incr>::remove(int index)
{
//T* t = storage[index];
//为什么使用operator[](index)来去索引index出的指针元素,因为重载后的[]运算符是安全的受检查的
T* t = operator[](index);
if (t != )
{
storage[index] = ;
}
return t;
} template<class T, int incr>
T* PStash<T, incr>::pop()
{
int top = next - ;
T* t = operator[](top);
if (t != )
{
storage[top] = ;
}
next--;
return t;
} template<class T, int incr>
void PStash<T, incr>::inflate(int increase)
{
const int psz = sizeof(T*); //int* a = new int[5];
//在对上分配一个长度为capacity + increase的T类型的指针数组
T** st = new T*[capacity + increase]; memset(st, , (capacity + increase) * psz);
memcpy(st, storage, capacity * psz); capacity += increase;
delete []storage; storage = st;
} template<class T, int incr>
PStash<T, incr>::~PStash()
{
int n = ;
std::cout << "------- ~PStash() ------" << std::endl;
//清除容器中剩余元素占用的内存空间
for (int i = ; i < next; i++)
{
T* ele = storage[i];
std::cout << ++n << ": " << ele << ": " << *ele << std::endl;
delete ele;
storage[i] = ;
} //清除inflate()中在堆上分配的指针数组占用的内存空间
delete []storage;
} #endif

2)测试文件

 #include "TPStash.h"
#include <iostream>
#include <string>
#include "Book.h" using namespace std; int main()
{
cout << endl << "---------- PStash<string, 5> ----------------------" << endl;
{
PStash<string, > strings; string* s1 = new string("hello");
string* s2 = new string("world"); cout << s1 << endl;
cout << s2 << endl; strings.add(s1);
strings.add(s2);
} cout << endl << "----------- PStash<Book, 5> ------------------------" << endl; {
Book* b1 = new Book("算法精解", "Kyle Loudon", 56.2);
Book* b2 = new Book("Qt程序设计", "Pulat", 10.2); PStash<Book, > books;
books.add(b1);
books.add(b2); cout << "book1: " << b1 << endl;
cout << "book2: " << b2 << endl; Book* bk3 = books.pop();
cout << "pop(): " << *bk3 << endl;
delete bk3; //从容器中取出来的Book指针,要负责清除该指针指向的Book对象
} cout << "------------- End --------------------------------" << endl; return ; };

运行结果:

指针版的PStash(用一个void指针数组, 来保存存入元素的地址) 附模板化实现 p321的更多相关文章

  1. JAVA生成一个二维数组,使中间元素不与相邻的9个元素相等,并限制每一个元素的个数

    JAVA生成一个二维数组,使中间元素不与相邻的9个元素相等,并限制每一个元素的个数 示例如下 至少需要九个元素:"A","B","C",&q ...

  2. 【跟着子迟品 underscore】如何优雅地写一个『在数组中寻找指定元素』的方法

    Why underscore (觉得这部分眼熟的可以直接跳到下一段了...) 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中. ...

  3. 你必须知道的指针基础-7.void指针与函数指针

    一.不能动的“地址”—void指针 1.1 void指针初探 void *表示一个“不知道类型”的指针,也就不知道从这个指针地址开始多少字节为一个数据.和用int表示指针异曲同工,只是更明确是“指针” ...

  4. void指针、NULL指针和未初始化指针

    一个指针可以被声明为void类型,比如void *x.一个指针可以被赋值为NULL.一个指针变量声明之后但没有被赋值,叫做未初始化指针. 1 2 3 4 5 6 7 8 9 10 11 12 13 1 ...

  5. void*指针

    C++提供了一种特殊的指针类型void*,它可以保存任何类型对象的地址: void*表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型. void*指针只支持几种有限的操作: 1)与另一个指 ...

  6. void类型及void指针(转载)

    转载 https://www.cnblogs.com/pengyingh/articles/2407267.html 2.void的含义 void的字面意思是“无类型”,void *则为“无类型指针” ...

  7. void指针意义、Const、volatile、#define、typedef、接续符

    1.C语言规定只有相同类型的指针才可以相互赋值. Void*指针作为左值用于接收任意类型的指针, void*指针作为右值赋给其他指针时需要强制类型转换. 2.在C语言中Const修饰的变量是只读的,本 ...

  8. void和void*指针的一些理解

    void 和 void* 指针分别表示无类型和无类型指针. void 的作用是限制: 1,函数无返回值. 2,函数无参数. 当函数的返还值无参数的时候一定要加上 void ,因为在缺省的状态下函数的返 ...

  9. C++ Primer 学习笔记与思考_7 void和void*指针的使用方法

    (一)void的含义 void的字面意思是"无类型",void差点儿仅仅有"凝视"和限制程序的作用,由于从来没有人会定义一个void变量,让我们试着来定义: v ...

随机推荐

  1. .Net Core 授权系统组件解析

    前面关于.Net Core如何进行用户认证的核心流程介绍完毕之后,.Net Core 认证系统之Cookie认证源码解析远程认证暂时不介绍,后期有时间,我会加上.接下去介绍认证组件是如何和认证组件一起 ...

  2. meta标签、利用媒体查询 link不同的CSS文件

    利用媒体查询 link不同的CSS文件:<link rel="stylesheet" media="screen and (min-width:1px) and ( ...

  3. 解决IE6、IE7、Firefox兼容最简单的CSS Hack

    写三句代码来控制一个属性,区别Firefox,IE7,IE6: background:orange; *background:green !important; *background:blue;   ...

  4. 移动端适配之二:visual viewport、layout viewport和ideal viewport介绍

    上一篇博文,可算把像素这个东西讲清楚了.在这篇博文里面,将继续介绍viewport相关的内容. 很多博客都会提到PPK所讲的三个viewport,有的讲的比较复杂,看的云里雾里,我这里也大概介绍一下, ...

  5. DOM事件机制

    前言 本文主要介绍DOM事件级别.DOM事件模型.事件流.事件代理和Event对象常见的应用,希望对你们有些帮助和启发! 本文首发地址为GitHub博客,写文章不易,请多多支持与关注! 一.DOM事件 ...

  6. Leetcode693.Binary Number with Alternating Bits交替位二进制数

    给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等. 示例 1: 输入: 5 输出: True 解释: 5的二进制数是: 101 示例 2: 输入: 7 输出 ...

  7. Redis、MPP、kafka 、MongDB简介

    Redis :间值数据库,适合缓存用户Session会话与经常需要查的数据1.Redis集群,为什么在项目中使用集群  1.持久化,持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要左右 ...

  8. Directx11教程36 纹理映射(6)

    原文:Directx11教程36 纹理映射(6)    本章主要是整理代码,做以下两件事情: 1.把世界坐标矩阵的计算,放在GraphicsClass的渲染函数中,之前放在D3DClass中,而且只是 ...

  9. React-FlipOver-Counter(日历翻页)

    跟窝一起学习鸭~~ //index.js import React from 'react'; import ReactDOM from 'react-dom'; import './index.cs ...

  10. More Effective C++: 04效率

    16:牢记80-20准则 80-20准则说的是大约20%的代码使用了80%的程序资源:大约20%的代码耗用了大约80%的运行时间:大约20%的代码使用了80%的内存:大约20%的代码执行80%的磁盘访 ...