【深入理解C++】类模板

1.类模板之类型参数

对于类模板来说,编译器无法进行自动类型推导,只能显式指定类型。

模板名称加上模板参数列表才是类名称,例如 SeqStack 是模板名称,SeqStack<T> 是类名称,实际上,我们在写代码时想用的肯定是类名 SeqStack<T>,但为了简便,在类模板内部可以直接使用模板名 SeqStack 并不需要提供模板参数列表 <T>

建议:构造函数名和析构函数名不用加模板参数列表 <T>,其它出现模板的地方都加上模板参数列表 <T>

注意:在老标准中,只能为类模板提供默认模板参数;在 C++11 新标准中,函数模板和类模板都可以提供默认模板参数。

1.1 实现按两倍方式扩容的顺序栈

#include <iostream>
using namespace std;

template <typename T>
class SeqStack
{
public:
	SeqStack(int size = 10)
		: m_data(new T[size])
		, m_top(0)
		, m_size(size)
	{ }

	~SeqStack()
	{
		delete[] m_data;
		m_data = nullptr;
	}

	SeqStack(const SeqStack<T>& stack)
		: m_top(stack.m_top)
		, m_size(stack.m_size)
	{
		m_data = new T[m_size];
		for (int i = 0; i < m_top; ++i) // 不要用memcopy进行拷贝,因为不知道T是什么类型
		{
			m_data[i] = stack.m_data[i];
		}
	}

	SeqStack<T>& operator=(const SeqStack<T>& stack)
	{
		if (this == &stack) return *this;
		delete[] m_data;
		m_top = stack.m_top;
		m_size = stack.m_size;
		m_data = new T[m_size];
		for (int i = 0; i < m_top; ++i) // 不要用memcopy进行拷贝,因为不知道T是什么类型
		{
			m_data[i] = stack.m_data[i];
		}
		return *this;
	}

	void push(const T& val)
	{
		if (full()) expand();
		m_data[m_top++] = val;
	}

	void pop()
	{
		if (empty()) return;
		--m_top;
	}

	// 如果某个方法只涉及读而不涉及写,我们尽量把它实现成const常方法,这样一来,普通对象可以调用,常对象也可以调用。
	T top() const
	{
		if (empty()) throw "stack is empty!"; // 抛异常也代表函数逻辑结束
		return m_data[m_top - 1];
	}

	bool full() const
	{
		return m_top == m_size;
	}

	bool empty() const
	{
		return m_top == 0;
	}

private:
	void expand()
	{
		T* new_data = new T[m_size * 2];
		for (int i = 0; i < m_top; ++i)
		{
			new_data[i] = m_data[i];
		}
		delete[] m_data;
		m_data = new_data;
		m_size *= 2;
	}

private:
	T* m_data;
	int m_top;
	int m_size;
};

int main()
{
	SeqStack<int> stk; // 显式类型指定
	stk.push(10);
	stk.push(20);
	stk.push(30);
	stk.pop();
	cout << stk.top() << endl;

	return 0;
}

1.2 类模板的成员函数

如上代码所示,类模板成员函数的实现可以写在类模板定义的里面。

需要注意的是,类模板的成员函数具有和这个类模板相同的模板参数,如果要把类模板成员函数的实现写在类模板定义的外面,那么必须以关键字 template 开始,后面加上模板参数列表,同时用带模板参数列表的类名修饰类模板的成员函数名。

SeqStack.h

#ifndef __SEQSTACK_H
#define __SEQSTACK_H

template <typename T>
class SeqStack
{
public:
	SeqStack(int size = 10);
	~SeqStack();
	SeqStack(const SeqStack<T>& stack);
	SeqStack<T>& operator=(const SeqStack<T>& stack);
	void push(const T& val);
	void pop();
	T top() const;
	bool full() const;
	bool empty() const;

private:
	void expand();

private:
	T* m_data;
	int m_top;
	int m_size;
};

template <typename T>
SeqStack<T>::SeqStack(int size)
	: m_data(new T[size])
	, m_top(0)
	, m_size(size)
{}

template <typename T>
SeqStack<T>::~SeqStack()
{
	delete[] m_data;
	m_data = nullptr;
}

template <typename T>
SeqStack<T>::SeqStack(const SeqStack<T>& stack)
	: m_top(stack.m_top)
	, m_size(stack.m_size)
{
	m_data = new T[m_size];
	for (int i = 0; i < m_top; ++i) // 不要用memcopy进行拷贝,因为不知道T是什么类型
	{
		m_data[i] = stack.m_data[i];
	}
}

template <typename T>
SeqStack<T>& SeqStack<T>::operator=(const SeqStack<T>& stack)
{
	if (this == &stack) return *this;
	delete[] m_data;
	m_top = stack.m_top;
	m_size = stack.m_size;
	m_data = new T[m_size];
	for (int i = 0; i < m_top; ++i) // 不要用memcopy进行拷贝,因为不知道T是什么类型
	{
		m_data[i] = stack.m_data[i];
	}
	return *this;
}

template <typename T>
void SeqStack<T>::push(const T& val)
{
	if (full()) expand();
	m_data[m_top++] = val;
}

template <typename T>
void SeqStack<T>::pop()
{
	if (empty()) return;
	--m_top;
}

// 如果某个方法只涉及读而不涉及写,我们尽量把它实现成const常方法,这样一来,普通对象可以调用,常对象也可以调用。
template <typename T>
T SeqStack<T>::top() const
{
	if (empty()) throw "stack is empty!"; // 抛异常也代表函数逻辑结束
	return m_data[m_top - 1];
}

template <typename T>
bool SeqStack<T>::full() const
{
	return m_top == m_size;
}

template <typename T>
bool SeqStack<T>::empty() const
{
	return m_top == 0;
}

template <typename T>
void SeqStack<T>::expand()
{
	T* new_data = new T[m_size * 2];
	for (int i = 0; i < m_top; ++i)
	{
		new_data[i] = m_data[i];
	}
	delete[] m_data;
	m_data = new_data;
	m_size *= 2;
}

#endif

main.cpp

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

int main()
{
	SeqStack<int> stk(15); // 显式类型指定
	stk.push(10);
	stk.push(20);
	stk.push(30);
	stk.pop();
	cout << stk.top() << endl;

	return 0;
}

2.类模板➡模板的实例化➡模板类

类模板:是不进行编译的,因为类型还不知道。

模板的实例化:在编译时,当编译器看到模板定义时,并不会立即产生代码,只有在看到模板调用时,编译器才会产生对应的实例。也就是说,模板如果没有被使用,是不会被实例化出来的。

类模板的选择性实例化:一个类模板可能有很多成员函数,但如果后续没有使用到某个成员函数,则这个成员函数不会被实例化。

模板类:是要被编译器所编译的。

3.类模板之非类型参数

#include <iostream>
using namespace std;

template <typename T, int SIZE = 10>
class SeqStack
{
public:
	SeqStack()
		: m_data(new T[SIZE])
		, m_top(0)
		, m_size(SIZE)
	{
	}

	~SeqStack()
	{
		delete[] m_data;
		m_data = nullptr;
	}

	SeqStack(const SeqStack<T>& stack)
		: m_top(stack.m_top)
		, m_size(stack.m_size)
	{
		m_data = new T[m_size];
		for (int i = 0; i < m_top; ++i) // 不要用memcopy进行拷贝,因为不知道T是什么类型
		{
			m_data[i] = stack.m_data[i];
		}
	}

	SeqStack<T>& operator=(const SeqStack<T>& stack)
	{
		if (this == &stack) return *this;
		delete[] m_data;
		m_top = stack.m_top;
		m_size = stack.m_size;
		m_data = new T[m_size];
		for (int i = 0; i < m_top; ++i) // 不要用memcopy进行拷贝,因为不知道T是什么类型
		{
			m_data[i] = stack.m_data[i];
		}
		return *this;
	}

	void push(const T& val)
	{
		if (full()) expand();
		m_data[m_top++] = val;
	}

	void pop()
	{
		if (empty()) return;
		--m_top;
	}

	// 如果某个方法只涉及读而不涉及写,我们尽量把它实现成const常方法,这样一来,普通对象可以调用,常对象也可以调用。
	T top() const
	{
		if (empty()) throw "stack is empty!"; // 抛异常也代表函数逻辑结束
		return m_data[m_top - 1];
	}

	bool full() const
	{
		return m_top == m_size;
	}

	bool empty() const
	{
		return m_top == 0;
	}

private:
	void expand()
	{
		T* new_data = new T[m_size * 2];
		for (int i = 0; i < m_top; ++i)
		{
			new_data[i] = m_data[i];
		}
		delete[] m_data;
		m_data = new_data;
		m_size *= 2;
	}

private:
	T* m_data;
	int m_top;
	int m_size;
};

int main()
{
	SeqStack<int, 15> stk; // 显式参数指定
	stk.push(10);
	stk.push(20);
	stk.push(30);
	stk.pop();
	cout << stk.top() << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值