CPP implements a custom template stack and overloads input and output operators
CPP 实现自定义模板栈
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素; 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
具体结构
实现一个特定类型的栈只能用于存储特定类型,功能很有限。就想写成模板的形式,这样通用性会更强一些。
- 先定义模板栈类的结构
const int defaultSize = 16;
template <typename T> class CStack {
public:
CStack(int);
~CStack();
bool empty();
bool full();
int size() const; /* 返回栈已使用的空间个数 */
int capacity() const; /* 返回栈的容量,开辟的空间数组的总大小 */
bool push(T); /* 入栈操作 */
bool pop(T &); /* 出栈操作 */
T operator[](int);
private:
void increaseCapacity(); /* 用于在栈空间满时,自动扩充栈 */
T *m_pData;
int m_nTop; /* 指向栈顶 */
int m_nBottom; /* 指向栈底 */
int m_nSize; /* 栈的空间大小 */
};
- 实现构造函数和析构函数
template <typename T> CStack<T>::CStack(int size) {
if (size <= 0)
size = defaultSize;
m_nSize = size;
m_pData = new T[size];
m_nTop = m_nBottom = 0;
}
template <typename T> CStack<T>::~CStack() { delete[] m_pData; }
- 实现入栈和出栈操作
/* size() const */
template <typename T> int CStack<T>::size() const { return m_nTop - m_nBottom; }
template <typename T> bool CStack<T>::full() { return m_nTop >= m_nSize; }
/* empty() */
template <typename T> bool CStack<T>::empty() {
return (m_nTop == m_nBottom) ? true : false;
}
template <typename T> bool CStack<T>::push(T ele) {
if (full())
increaseCapacity();
m_pData[m_nTop++] = ele;
return true;
}
template <typename T> bool CStack<T>::pop(T &ele) {
if (empty())
return false;
ele = m_pData[--m_nTop];
return true;
}
- 实现索引访问
template <typename T> T CStack<T>::operator[](int idx) {
if (idx < m_nBottom || idx >= m_nTop)
std::exit(-1);
return m_pData[idx];
}
- 实现栈空间自增
template <typename T> int CStack<T>::capacity() const { return m_nSize; }
template <typename T> void CStack<T>::increaseCapacity() {
m_nSize = m_nSize * 2;
T *tmp_nData = new T[m_nSize];
for (int i = 0; i < m_nTop; i++) {
tmp_nData[i] = m_pData[i];
}
delete[] m_pData;
m_pData = tmp_nData;
}
重载输入输出操作符
在C++中,I/O使用了流的概念-字符(或字节)流。我们可以用 cin 从命令行读取信息,也可以用 cout 把信息输出到标准输出流。 那么我们能不能用流操作符实现对自定义栈的操作呢,用 » 来读取信息到栈,用 « 来从栈中起出数据。
- 先看代码
void printStack(CStack<string> &sta) {
cout << "Print stack: ";
for (int i = 0; i < sta.size(); i++) {
cout << sta[i] << " ";
}
cout << endl;
}
int main() {
std::stringstream ss1("hello"),ss2("world");
CStack<string> sta(3);
ss1 >> sta;
ss2 >> sta;
printStack(sta);
cout << sta << endl;
printStack(sta);
return 0;
}
- 结果如下
Print stack: hello world
world
Print stack: hello
类模板配合友元函数实现
类内实现
- 在模板类内部直接声明友元函数,不涉及函数模板
这种情况只能在模板类内部一起把函数的定义写出来,不能在外部实现,因为外部需要类型参数,而需要类型参数就是模板了。 其实这种情况相当于一般的模板类的成员函数,也就是相当于一个函数模板
template <typename T> class CStack {
// other code
public:
/* 全局函数配合友元 类内实现 */
friend std::ostream &operator<<(std::ostream &out, CStack<T> &cstack) {
T ele;
cstack.pop(ele);
out << ele;
return out;
}
friend std::istream &operator>>(std::istream &in, CStack<T> &s) {
T ele;
in >> ele;
s.push(ele);
return in;
}
};
类外实现1
- 在模板类内部声明对应版本的友元函数模板实例化时,需要前置声明
该方法声明一个函数模板,并保持该函数的参数类型和该模板类的实例一个类型。 注意 <> 符号的使用,一是,表明此友元函数是函数模板;二是,此模板使用的模板类型参数为当前模板类的类型参数class。
不加 <> 会报错:friend declaration ‘std::ostream& operator«(std::ostream&, CStack
/* 在 Stack 类外 */
template <typename T> class CStack; /* 要对类进行声明 要声明在模板之前 */
template <typename T> std::ostream &operator<<(std::ostream &, CStack<T> &);
template <typename T> std::istream &operator>>(std::istream &, CStack<T> &);
/* 在 Stack 类内部 */
template <typename T> class CStack {
/* other code */
public:
/* 全局函数配合友元 类外实现 */
friend std::ostream &operator<<<>(std::ostream &out, CStack<T> &);
friend std::istream &operator>><T>(std::istream &in, CStack<T> &);
};
/* 函数具体实现 */
template <typename T>
inline std::ostream &operator<<(std::ostream &out_stream, CStack<T> &cstack)
{
T ele;
cstack.pop(ele);
out_stream << ele;
return out_stream;
}
template <typename T>
inline std::istream &operator>>(std::istream &in_stream, CStack<T> &cstack) {
T ele;
in_stream >> ele;
cstack.push(ele);
return in_stream;
}
类外实现2
- 在模板类内部声明友元的函数模板
这种方式更为灵活,它不会要求参数类型与模板类实例是一个类型,但是一般情况下我们也是按照一个类型使用的。
template <typename T> class CStack {
/* other code */
public:
template <typename U>
friend std::ostream &operator<<(std::ostream &out, CStack<U> &);
template <typename U>
friend std::istream &operator>>(std::istream &in, CStack<U> &);
};
/* 具体实现 */
template <typename T>
inline std::ostream &operator<<(std::ostream &out_stream, CStack<T> &cstack) {
T ele;
cstack.pop(ele);
out_stream << ele;
return out_stream;
}
template <typename T>
inline std::istream &operator>>(std::istream &in_stream, CStack<T> &cstack) {
T ele;
in_stream >> ele;
cstack.push(ele);
return in_stream;
}