重新组织PStash代码成为模板并不简单,因为一些常用函数不应当内联
除了增量的大小已经被模板化为具有默认值的无类参数以为,所以这个增量
的大小能在实例时修改
//: C16:TPStash.h
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
#ifndef TPSTASH_H
#define TPSTASH_H
template<class T, int incr = 10>
class PStash {
int quantity; // Number of storage spaces
int next; // Next empty space
T** storage;
void inflate(int increase = incr);
public:
PStash() : quantity(0), next(0), storage(0) {}
~PStash();
int add(T* element);
T* operator[](int index) const; // Fetch
// Remove the reference from this PStash:
T* remove(int index);
// Number of elements in Stash:
int count() const { return next; }
};
template<class T, int incr>
int PStash<T, incr>::add(T* element) {
if(next >= quantity)
inflate(incr);
storage[next++] = element;
return(next - 1); // Index number
}
// Ownership of remaining pointers:
template<class T, int incr>
PStash<T, incr>::~PStash() {
for(int i = 0; i < next; i++) {
delete storage[i]; // Null pointers OK
storage[i] = 0; // Just to be safe
}
delete []storage;
}
template<class T, int incr>
T* PStash<T, incr>::operator[](int index) const {
require(index >= 0,
"PStash::operator[] index negative");
if(index >= next)
return 0; // To indicate the end
require(storage[index] != 0,
"PStash::operator[] returned null pointer");
// Produce pointer to desired element:
return storage[index];
}
template<class T, int incr>
T* PStash<T, incr>::remove(int index) {
// operator[] performs validity checks:
T* v = operator[](index);
// "Remove" the pointer:
if(v != 0) storage[index] = 0;
return v;
}
template<class T, int incr>
void PStash<T, incr>::inflate(int increase) {
const int psz = sizeof(T*);
T** st = new T*[quantity + increase];
memset(st, 0, (quantity + increase) * psz);
memcpy(st, storage, quantity * psz);
quantity += increase;
delete []storage; // Old storage
storage = st; // Point to new memory
}
#endif // TPSTASH_H ///:~
在这里使用的默认增量大小是很小的,以便保证能发生对inflate()的调用
为了测试模板化的PStash的控制权,下面的类将报告自身的创建和销毁,并
保证被创建的对象都能被销毁
AutoCounter只允许它的类型的对象在栈上创建
//: C16:AutoCounter.h
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
#ifndef AUTOCOUNTER_H
#define AUTOCOUNTER_H
#include "../require.h"
#include <iostream>
#include <set> // Standard C++ Library container
#include <string>
class AutoCounter {
static int count;
int id;
class CleanupCheck {
std::set<AutoCounter*> trace;
public:
void add(AutoCounter* ap) {
trace.insert(ap);
}
void remove(AutoCounter* ap) {
require(trace.erase(ap) == 1,
"Attempt to delete AutoCounter twice");
}
~CleanupCheck() {
std::cout << "~CleanupCheck()"<< std::endl;
require(trace.size() == 0,
"All AutoCounter objects not cleaned up");
}
};
static CleanupCheck verifier;
AutoCounter() : id(count++) {
verifier.add(this); // Register itself
std::cout << "created[" << id << "]"
<< std::endl;
}
// Prevent assignment and copy-construction:
AutoCounter(const AutoCounter&);
void operator=(const AutoCounter&);
public:
// You can only create objects with this:
static AutoCounter* create() {
return new AutoCounter();
}
~AutoCounter() {
std::cout << "destroying[" << id
<< "]" << std::endl;
verifier.remove(this);
}
// Print both objects and pointers:
friend std::ostream& operator<<(
std::ostream& os, const AutoCounter& ac){
return os << "AutoCounter " << ac.id;
}
friend std::ostream& operator<<(
std::ostream& os, const AutoCounter* ac){
return os << "AutoCounter " << ac->id;
}
};
#endif // AUTOCOUNTER_H ///:~
AutoCounter类做两件事 第一,它继续对AutoCounter的每个实例编号:这个
编号的值保存在id中,并且使用static数据成员count来生成这个编号
第二更复杂,嵌套类CleanupCheck的一个静态实例跟踪被创建和销毁的所有
的AutoCounter对象,如果程序员没有完全清除它们,它就向程序员报告
set类是按照它所包含的类型建立模板的
在remove()中,使用set::erase()从set中移出AutoCounter指针
CleanupCheck的析构函数最后检查set的长度是否确实是0
AutoCounter的构造函数和析构函数用verifier对象注册和注销它们自己
因为所有的成员函数都是内联的,所以使用实现文件的唯一原因是为了包含
静态数据成员的定义
//: C16:AutoCounter.cpp {O}
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Definition of static class members
#include "AutoCounter.h"
AutoCounter::CleanupCheck AutoCounter::verifier;
int AutoCounter::count = 0;
///:~
利用手边的AutoCounter,我们可以测试PStash的功能
不仅表明PStash析构函数清除了它现在所拥有的对象,还表明AutoCounter
类如何检测到还没有被清除的对象
//: C16:TPStashTest.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
//{L} AutoCounter
#include "AutoCounter.h"
#include "TPStash.h"
#include <iostream>
#include <fstream>
using namespace std;
int main() {
PStash<AutoCounter> acStash;
for(int i = 0; i < 10; i++)
acStash.add(AutoCounter::create());
cout << "Removing 5 manually:" << endl;
for(int j = 0; j < 5; j++)
delete acStash.remove(j);
cout << "Remove two without deleting them:"
<< endl;
// ... to generate the cleanup error message.
cout << acStash.remove(5) << endl;
cout << acStash.remove(6) << endl;
cout << "The destructor cleans up the rest:"
<< endl;
// Repeat the test from earlier chapters:
ifstream in("TPStashTest.cpp");
assure(in, "TPStashTest.cpp");
PStash<string> stringStash;
string line;
while(getline(in, line))
stringStash.add(new string(line));
// Print out the strings:
for(int u = 0; stringStash[u]; u++)
cout << "stringStash[" << u << "] = "
<< *stringStash[u] << endl;
getchar();
} ///:~
当从PStash中移出AutoCounter元素5和元素6时,它们就变成了调用者的责任,
但是因为调用者没有清除它们,所以就引起了内存泄漏,随后在运行时被
AutoCounter检测到
输出
created[0]
created[1]
created[2]
created[3]
created[4]
created[5]
created[6]
created[7]
created[8]
created[9]
Removing 5 manually:
destroying[0]
destroying[1]
destroying[2]
destroying[3]
destroying[4]
Remove two without deleting them:
AutoCounter 5
AutoCounter 6
The destructor cleans up the rest:
stringStash[0] = //: C16:TPStashTest.cpp
stringStash[1] = // From Thinking in C++, 2nd Edition
stringStash[2] = // Available at http://www.BruceEckel.com
stringStash[3] = // (c) Bruce Eckel 2000
stringStash[4] = // Copyright notice in Copyright.txt
stringStash[5] = //{L} AutoCounter
stringStash[6] = #include "AutoCounter.h"
stringStash[7] = #include "TPStash.h"
stringStash[8] = #include <iostream>
stringStash[9] = #include <fstream>
stringStash[10] = using namespace std;
stringStash[11] =
stringStash[12] = int main() {
stringStash[13] = PStash<AutoCounter> acStash;
stringStash[14] = for(int i = 0; i < 10; i++)
stringStash[15] = acStash.add(AutoCounter::create());
stringStash[16] = cout << "Removing 5 manually:" << endl;
stringStash[17] = for(int j = 0; j < 5; j++)
stringStash[18] = delete acStash.remove(j);
stringStash[19] = cout << "Remove two without deleting them:"
stringStash[20] = << endl;
stringStash[21] = // ... to generate the cleanup error message.
stringStash[22] = cout << acStash.remove(5) << endl;
stringStash[23] = cout << acStash.remove(6) << endl;
stringStash[24] = cout << "The destructor cleans up the rest:"
stringStash[25] = << endl;
stringStash[26] = // Repeat the test from earlier chapters:
stringStash[27] = ifstream in("TPStashTest.cpp");
stringStash[28] = assure(in, "TPStashTest.cpp");
stringStash[29] = PStash<string> stringStash;
stringStash[30] = string line;
stringStash[31] = while(getline(in, line))
stringStash[32] = stringStash.add(new string(line));
stringStash[33] = // Print out the strings:
stringStash[34] = for(int u = 0; stringStash[u]; u++)
stringStash[35] = cout << "stringStash[" << u << "] = "
stringStash[36] = << *stringStash[u] << endl;
stringStash[37] = getchar();
stringStash[38] = } ///:~