从零开始学C++之模板(二):类模板、Stack的类模板实现(自定义链栈方式,自定义数组方式)

一、类模板

类模板:将类定义中的数据类型参数化 类模板实际上是函数模板的推广,可以用相同的类模板来组建任意类型的对象集合

(一)、类模板的定义

template  <类型形参表> class  <类名> {     //类说明体  }; template  <类型形参表> <返回类型> <类名> <类型名表>::<成员函数1>(形参表) {     //成员函数定义体  } template  <类型形参表> <返回类型> <类名> <类型名表>::<成员函数2>(形参表) {     //成员函数定义体  } … template  <类型形参表> <返回类型> <类名> <类型名表>::<成员函数n>(形参表) {     //成员函数定义体  }

(二)、使用类模板

类模板的实例化:用具体的数据类型替换模板的参数以得到具体的类(模板类) 模板类也可以实例化为对象 用下列方式创建类模板的实例: 类名 <类型实参表>  对象名称;

对于函数模板与类模板,模板参数并不局限于类型(类类型,基本类型,模板类实例),普通值也可以作为模板参数

二、Stack类的模板实现

前面曾经分别使用C/C++实现了一个链栈,栈中只能放进int类型数据,现在使用模板来重新实现Stack,可以存放多种数据类型,分别使用自定义链栈方式以及自定义数组实现。

(一)、自定义链栈方式:

stack.h:

/*************************************************************************
> File Name: stack.h
> Author: Simba
> Mail: dameng34@163.com
> Created Time: 2012年11月03日 星期六 19时28分25秒
************************************************************************/

#include<iostream>
using namespace std;
template < class T > class Node
{
    //< >里面是模板参数,可以有多个,虽然T用class 声明,但可以是内建类型也可以是class类型
    //模板的定义一般写在头文件里
public:

    Node(T invalue): m_Value(invalue), m_Next(NULL) {}
    ~Node();

    T getValue() const
    {
        return m_Value;
    }
    void setValue(T value)
    {
        m_Value = value;
    }

    Node < T > *getNext() const
    {
        return m_Next;
    }
    void setNext(Node < T > *next)
    {
        m_Next = next;
    }

private:
    T m_Value;
    Node < T > *m_Next;
};

template < class T > Node < T >::~Node()
{
    if (m_Next)
    {
        delete m_Next;  //自动内存管理,接着找到m_Next指向的下一个结点,一直找到最后的一个结点
        //故先释放最后一个结点(当然是最先压栈的结点),然后依次返回释放每一个途中的结点
    }
    cout << m_Value << " deleted " << endl;
}

template < class T > class Stack
{

public:
    Stack(): m_Head(NULL), m_Count(0) {}
    ~Stack()
    {
        delete m_Head;  //自动内存管理
    }
    void push(const T &t);
    T pop();
    T top() const;
    int count() const;
private:
    Node < T > *m_Head;
    int m_Count;
};

template < class T > void Stack < T >::push(const T &value)
{
    Node < T > *newNode = new Node < T > (value);
    newNode->setNext(m_Head);
    m_Head = newNode;
    ++m_Count;
}

template < class T > T Stack < T >::pop()
{
    Node < T > *popped = m_Head;
    if (m_Head != NULL)
    {

        m_Head = m_Head->getNext();
        T retval = popped->getValue();
        popped->setNext(NULL);
        delete popped;
        --m_Count;
        return retval;
    }
    return 0;
}

template < class T > inline T Stack < T >::top() const
//模板前缀template < class T >  || 函数限定符inline  函数返回值T ||
//命名空间前缀 Stack < T > //一个类型  || 函数名(函数参数)const 限定符
{
    return m_Head->getValue();

}

template < class T > inline int Stack < T >::count() const
{
    return m_Count;
}

main.cpp:

#include "stack.h"

int main(void)
{
    Stack < int >intstack1, intstack2;

    int val;
    for (val = 0; val < 4; ++val)
    {
        intstack1.push(val);
        intstack2.push(2 * val);
    }

    while (intstack1.count())
    {
        val = intstack1.pop();
        cout << val << endl;
    }

    Stack < char >stringstack;

    stringstack.push('A');
    stringstack.push('B');
    stringstack.push('C');

    char val2;
    while (stringstack.count())
    {
        val2 = stringstack.pop();
        cout << val2 << endl;
    }
    cout << "Now intstack2 will be destructed." << endl;
    return 0;
}

可以看到虽然intstack2 没有pop 出元素,但程序结束时,局部对象会被析构,调用析构函数,在析构函数内delete 头指针,顺藤摸瓜一直找到最后一个节点,即首先压栈的节点,依次返回释放掉。

(二)、自定义数组方式

Stack2.h:

#ifndef _STACK2_H_
#define _STACK2_H_

#include <exception>

template <typename T, int MAX_SIZE>
class Stack2
{
public:
    Stack2();
    ~Stack2();

    void Push(const T &elem);
    void Pop();
    T &Top();
    const T &Top() const;
    bool Empty() const;
private:
    T *elems_;
    int top_;
};

template <typename T, int MAX_SIZE>
Stack2<T, MAX_SIZE>::Stack2() : top_(-1)
{
    elems_ = new T[MAX_SIZE];
}

template <typename T, int MAX_SIZE>
Stack2<T, MAX_SIZE>::~Stack2()
{
    delete[] elems_;
}

template <typename T, int MAX_SIZE>
void Stack2<T, MAX_SIZE>::Push(const T &elem)
{
    if (top_ + 1 >= MAX_SIZE)
        throw out_of_range("Stack2<>::Push() Stack2 full");

    elems_[++top_] = elem;
}

template <typename T, int MAX_SIZE>
void Stack2<T, MAX_SIZE>::Pop()
{
    if (top_ + 1 == 0)
        throw out_of_range("Stack2<>::Push() Stack2 empty");

    --top_;
}

template <typename T, int MAX_SIZE>
T &Stack2<T, MAX_SIZE>::Top()
{
    if (top_ + 1 == 0)
        throw out_of_range("Stack2<>::Push() Stack2 empty");

    return elems_[top_];
}

template <typename T, int MAX_SIZE>
const T &Stack2<T, MAX_SIZE>::Top() const
{
    if (top_ + 1 == 0)
        throw out_of_range("Stack2<>::Push() Stack2 empty");

    return elems_[top_];
}

template <typename T, int MAX_SIZE>
bool Stack2<T, MAX_SIZE>::Empty() const
{
    return top_ + 1 == 0;
}
#endif // _STACK2_H_

main.cpp:

#include "Stack2.h"
#include <iostream>
#include<string>
using namespace std;

int main(void)
{
    Stack2<int, 5> s;
    s.Push(1);
    s.Push(2);
    s.Push(3);

    while (!s.Empty())
    {
        cout << s.Top() << endl;
        s.Pop();
    }
    return 0;
}

输出为 3 2 1 

注意,用数组实现时pop 操作并没有删除元素的操作,只是移动了top 指针,下次push 的时候直接覆盖即可。再者因为实现了Top 返回栈顶元素,故pop 没有返回值。

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Ryan Miao

Java String.split()用法小结

在java.lang包中有String.split()方法,返回是一个数组 我在应用中用到一些,给大家总结一下,仅供大家参考: 1、如果用“.”作为分隔的话,必...

32811
来自专栏十月梦想

JavaScript中类的创建以及类的传参

在之前(ES2015)以前我们常用构造函数来搞定一个事物类,通过new 这个构造函数实现类的功能!在ES6(ES2015)中已经可以使用类,下面我们看一下类如何...

792
来自专栏GreenLeaves

JS框架设计之对象类型判断一种子模块

Javascript有两套数据类型,一套是基础数据类型,一套是对象数据类型。基础数据类型包括5种基本数据类型,分别是null,bool,undefined,nu...

1858
来自专栏Ryan Miao

String.split()用法以及特殊分隔符注意,ps:|

转载:http://www.cnblogs.com/mingforyou/archive/2013/09/03/3299569.html 在java.lang包...

2929
来自专栏web前端教室

web前端零基础课-0908*福祥-学习笔记

学了部分js内容后,完成了网站首页部分动态效果(搜索栏、侧边导航条、轮播图),先用最基本的,冗余最多的一步步实现;后面对Js进行了初步的封装,重新构建了Js文件...

733
来自专栏Golang语言社区

Go语言基本语法

前面已经看到了Go程序的基本结构,所以这将是很容易理解Go编程语言等基本构建块。 Go令牌 Go程序包括各种令牌和令牌可以是一个关键字,一个标识符,常量,字符串...

2646
来自专栏服务端技术杂谈

Java编码规范

命名 类名使用UpperCamelCase风格。 领域模型相关命名:DO / DTO / VO / DAO等。 方法名,参数名,成员变量,局部变量都统一使用lo...

2804
来自专栏柠檬先生

Sass 基础(五)

@if   @if 指令是一个SassScript,它可以根据条件处理样式块,如果条件为true返回一个样式块,反之   false 返回另一个样式块,在S...

2208
来自专栏desperate633

LintCode 单词切分题目分析

给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词。

762
来自专栏数据结构与算法

洛谷 P2679 子串

题目背景 无 题目描述 有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 ...

3114

扫码关注云+社区