C++编程思想 第1卷 第16章 模板介绍 作为模板的Stash和Stack 模板化的指针Stash

重新组织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] = } ///:~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值