由容器PStash的使用者,负责清除容器中的所有指针。所以用户必须记住放到容器中的是什么类型,在取出时,把取出的void指针转换成对应的类型指针,然后 'delete 转换后的对象指针',才能在清除时调到对象的析构函数。
析构函数的作用: 确保对象的各部分被正确的清除,及做一些用户指定的其他清理工作。
1 头文件PStash.h
1 #ifndef PSTASH_H 2 #define PSTASH_H 3 4 class PStash 5 { 6 int capacity; 7 int next; 8 void** storage; //void* st[]; void** storage = st;可以看是一个'指向指针数组'首元素的指针 9 void inflate(int increase); 10 public: 11 PStash() : capacity(0), next(0), storage(0) {} 12 ~PStash(); 13 14 int add(void* element); 15 void* operator[](int index) const; 16 void* remove(int index); 17 int count() const { return next; } 18 }; 19 #endif
2 PStash.cpp
1 #include "PStash.h" 2 #include "../require.h" 3 #include <iostream> 4 #include <cstring> 5 6 7 using namespace std; 8 9 PStash::~PStash() 10 { 11 for (int i = 0; i < next; i++) 12 { 13 //如果storage[i] == 0为false,即指针值storage[0,1,2,3,4]有不为0的,表明存在没有清除的元素 14 require(storage[i] == 0, "PStash not cleaned up"); 15 } 16 17 // void** st = new void*[capacity + increase]; 18 // storage 是void类型指针的数组,即数组storage的元素是void型指针 19 delete []storage; //清除指针数组这个容器 20 storage = 0; 21 cout << "after delete []storage;" << endl; 22 } 23 24 25 int PStash::add(void* element) 26 { 27 const int ssize = 10; 28 if (next >= capacity) 29 inflate(ssize); 30 cout << "storage[" << next << "] = " << element << endl; 31 storage[next++] = element; 32 return next - 1; 33 } 34 35 36 37 //重载[]运算符, intPStash[i] 返回值是void类型指针 38 void* PStash::operator [](int index) const 39 { 40 require(index >= 0, "PStash::operator[] index negative"); 41 if (index >= next) 42 return 0; 43 44 return storage[index]; 45 } 46 47 48 //把栈中index索引处的元素(指针)清零 49 void* PStash::remove(int index) 50 { 51 void* v = operator[](index); 52 if (v != 0) 53 storage[index] = 0; 54 return v; 55 } 56 57 58 void PStash::inflate(int increase) 59 { 60 const int psz = sizeof(void*); //地址占4个字节 61 62 //创建了一个void*数组(void型指针的数组) 63 //该语句在堆上一口气创建capacity + increase个void指针 64 void** st = new void*[capacity + increase]; //new 一个包含 capacity + 10个void*元素的指针数组 65 66 memset(st, 0, (capacity + increase) * psz); //该指针数组st共占(capacity + increase) * psz个字节 67 68 //storage是void类型指针数组,所有拷贝的是数组中元素(指针),是地址拷贝,不存在清除对象问题 69 //拷贝完后,要清除这个废弃的指针数组 70 memcpy(st, storage, capacity * psz); //把从storage地址开始的capacity * psz字节内容拷贝到st地址空间 71 72 capacity += increase; 73 delete []storage; 74 storage = st; 75 }
3 测试文件PStashTest.cpp -- PStash使用者
1 #include "PStash.h" 2 #include "../require.h" 3 #include <iostream> 4 #include <fstream> 5 #include <string> 6 #include "Book.h" 7 8 using namespace std; 9 10 int main() 11 { 12 { 13 PStash intStack; 14 for (int i = 0; i < 5; i++) 15 { 16 intStack.add(new int(i)); 17 } 18 for (int k = 0; k < 5; k++ ) 19 { 20 delete intStack.remove(k); 21 } 22 } 23 24 cout << "--------------------------------" << endl; 25 26 { 27 PStash strings; 28 29 string* s1 = new string("hello"); 30 string* s2 = new string("world"); 31 32 cout << s1 << endl; 33 cout << s2 << endl; 34 35 strings.add(s1); 36 strings.add(s2); 37 38 delete (string*) strings.remove(0); 39 delete (string*) strings.remove(1); 40 } 41 42 cout << "-----------------------------------" << endl; 43 44 { 45 Book* b1 = new Book("算法精解", "Kyle Loudon", 56.2); 46 Book* b2 = new Book("Qt程序设计", "Pulat", 10.2); 47 48 PStash books; 49 books.add(b1); 50 books.add(b2); 51 52 cout << "book1: " << b1 << endl; 53 cout << "book2: " << b2 << endl; 54 delete (Book*) books.remove(0); 55 delete books.remove(1); //books.remove(1)返回的是void型指针,所以此次的delete不会调用Book类的析构函数,而仅仅是释放了内容 56 //通常在析构函数中,会完成一些其他操作 57 } 58 59 cout << "--------------- end ---------------" << endl; 60 return 0; 61 };
运行结果:
运行过程分析:
附Book类定义
Book.h
1 #ifndef BOOK_H 2 #define BOOK_H 3 #include <string> 4 5 using std::string; 6 7 class Book 8 { 9 10 string name; 11 string author; 12 double price; 13 14 public: 15 Book(); 16 Book(string name, string author, double price); 17 18 //复制构造函数 19 Book(const Book& b); 20 21 ~Book(); 22 23 //把重载的<<运算符全局函数声明为友元 24 friend std::ostream& operator<<(std::ostream& os, const Book& b) 25 { 26 return os << "BookName: " << b.name << ", BookAuthor: " << b.author << ", BookPrice: " << b.price; 27 } 28 29 //重载赋值运算符 30 Book& operator=(const Book& b); 31 }; 32 #endif
Book.cpp
1 #include "Book.h" 2 #include <iostream> 3 4 using namespace std; 5 6 7 Book::Book() : name("null"), author("null"), price(0) 8 { 9 cout << "invoke constructor Book() " << endl; 10 } 11 12 13 Book::Book(string name, string author, double price) : name(name), author(author), price(price) 14 { 15 cout << "invoke constructor Book(string " << name << ", string " << author << ", double "<< price << ") " << endl; 16 } 17 18 //复制构造函数 19 Book::Book(const Book& b) : name(b.name), author(b.author), price(b.price) 20 { 21 cout << "Book::Book(const Book& b)" << endl; 22 } 23 24 Book::~Book() 25 { 26 cout << "~Book()" << endl; 27 cout << "free book: '" << name << "'" << endl; 28 } 29 30 31 //重载赋值运算符 32 Book& Book::operator=(const Book& b) 33 { 34 cout << "Book::operator=(const Book&)" << endl; 35 name = b.name; 36 author = b.author; 37 price = b.price; 38 39 return *this; 40 }
附, 模板化实现, 当模板化容器对象超出作用域时,能够负责清理容器中剩余的指针元素指向的对象
--- 因为模板化容器知道容器中存放元素的类型 (PStash<Book>,在目标特化时,容器中元素的类型已限定)
1)模板定义文件TPStash.h
1 #ifndef TPSTASH_H 2 #define TPSTASH_H 3 4 #include "../require.h" 5 6 template<class T, int incr = 10> 7 class PStash 8 { 9 int capacity; 10 int next; 11 T** storage; 12 void inflate(int increase = incr); 13 14 public: 15 16 PStash() : capacity(0), next(0), storage(0) {} 17 ~PStash(); 18 19 int add(T* element); 20 T* operator[](int index) const; 21 T* remove(int index); 22 T* pop(); 23 int count() const { return next; } 24 }; 25 26 27 28 //插入T型的指针元素element到容器,并返回插入位置索引 29 template<class T, int incr> 30 int PStash<T, incr>::add(T* element) 31 { 32 if (next >= capacity) 33 inflate(incr); 34 35 storage[next++] = element; 36 return next - 1; 37 } 38 39 40 //重载运算符[] 41 // T* ele = pstash[2] 42 // 入参: int, 返回值类型: T*, 该容器插入和取出的都是T类型的指针 43 template<class T, int incr> 44 T* PStash<T, incr>::operator[](int index) const 45 { 46 //若index >= 0为false,则向stderr打印错误提示信息"PStash::operator[] index negative",并终止程序的执行 47 require(index >= 0, "PStash::operator[] index negative"); 48 49 if (index >= next) 50 return 0; 51 52 require(storage[index] != 0, "PStash::operator[] returned null pointer"); 53 54 return storage[index]; 55 } 56 57 template<class T, int incr> 58 T* PStash<T, incr>::remove(int index) 59 { 60 //T* t = storage[index]; 61 //为什么使用operator[](index)来去索引index出的指针元素,因为重载后的[]运算符是安全的受检查的 62 T* t = operator[](index); 63 if (t != 0) 64 { 65 storage[index] = 0; 66 } 67 return t; 68 } 69 70 71 template<class T, int incr> 72 T* PStash<T, incr>::pop() 73 { 74 int top = next - 1; 75 T* t = operator[](top); 76 if (t != 0) 77 { 78 storage[top] = 0; 79 } 80 next--; 81 return t; 82 } 83 84 85 template<class T, int incr> 86 void PStash<T, incr>::inflate(int increase) 87 { 88 const int psz = sizeof(T*); 89 90 //int* a = new int[5]; 91 //在对上分配一个长度为capacity + increase的T类型的指针数组 92 T** st = new T*[capacity + increase]; 93 94 memset(st, 0, (capacity + increase) * psz); 95 memcpy(st, storage, capacity * psz); 96 97 capacity += increase; 98 delete []storage; 99 100 storage = st; 101 } 102 103 104 template<class T, int incr> 105 PStash<T, incr>::~PStash() 106 { 107 int n = 0; 108 std::cout << "------- ~PStash() ------" << std::endl; 109 //清除容器中剩余元素占用的内存空间 110 for (int i = 0; i < next; i++) 111 { 112 T* ele = storage[i]; 113 std::cout << ++n << ": " << ele << ": " << *ele << std::endl; 114 delete ele; 115 storage[i] = 0; 116 } 117 118 //清除inflate()中在堆上分配的指针数组占用的内存空间 119 delete []storage; 120 } 121 122 #endif
2)测试文件
1 #include "TPStash.h" 2 #include <iostream> 3 #include <string> 4 #include "Book.h" 5 6 using namespace std; 7 8 9 int main() 10 { 11 cout << endl << "---------- PStash<string, 5> ----------------------" << endl; 12 { 13 PStash<string, 5> strings; 14 15 string* s1 = new string("hello"); 16 string* s2 = new string("world"); 17 18 cout << s1 << endl; 19 cout << s2 << endl; 20 21 22 strings.add(s1); 23 strings.add(s2); 24 } 25 26 cout << endl << "----------- PStash<Book, 5> ------------------------" << endl; 27 28 { 29 Book* b1 = new Book("算法精解", "Kyle Loudon", 56.2); 30 Book* b2 = new Book("Qt程序设计", "Pulat", 10.2); 31 32 PStash<Book, 5> books; 33 books.add(b1); 34 books.add(b2); 35 36 cout << "book1: " << b1 << endl; 37 cout << "book2: " << b2 << endl; 38 39 Book* bk3 = books.pop(); 40 cout << "pop(): " << *bk3 << endl; 41 delete bk3; //从容器中取出来的Book指针,要负责清除该指针指向的Book对象 42 } 43 44 cout << "------------- End --------------------------------" << endl; 45 46 return 0; 47 48 };
运行结果: