C++之模板

114 阅读4分钟
  1. 函数模板的基本概念

    template <typename T>
    void Swap(T& a, T& b) {
    	T tmp = a;
    	a = b;
    	b = tmp;
    }
    
    Swap<int>(a, b);  // 手动指定使用int数据类型的模板
    Swap(a, b); // 编译器自动推导
    
  2. 函数模板的注意事项

    1. 虚函数和析构函数不能使用模板,其他函数可以,包括构造函数
    class Student {
    public:
    	template <typename T>
    	Student(T a) {
    		cout << "a=" << a << endl;
    	}
    
    	template <typename T>
    	void show(T b) {
    		cout << "b=" << b << endl;
    	}	
    };
    
    1. 函数模板也可以重载
    template <typename T>
    void func(T a) {
    	cout << "a = " << a << endl;
    }
    
    template <typename T1, typename T2>
    void func(T1 a, T2 b) {
    	cout << "a = " << a << ", b = " << b << endl;
    }
    
    template <typename T1, typename T2>
    void func(T1 a, T2 b, int c) {
    	cout << "a = " << a << ", b = " << b << ", c = " << c <<  endl;
    }
    
    1. 使用函数模板时,如果显示指定了函数模板的数据类型,那么可以发生隐式类型转换
    template <typename T>
    void show(T a, T b) {
    	cout << a << " " << b << endl;
    }
    
    int main() {
    	
    	int a = 10;
    	char b = 20;
    	show<int>(a, b); // 自动将b转换成int
    
    	return 0;
    }
    
    
  3. 函数模板的具体化

    1. 就是函数模板中的一种特殊情况,指定参数的类型
    template <typename T>
    void show(T a, T b) {
    	cout << "show(T a, T b)" << endl;
    }
    
    template <>
    void show(int a, int b) {
    	cout << "show(int a, int b)" << endl;
    }
    
    int main() {
    	
    	int a = 10;
    	int b = 20;
    	show(a, b);
    
    	return 0;
    }
    
    1. 函数调用优先级问题
    1. 普通函数 > 具体化 > 函数模板
    2. 如果函数模板能更好的匹配,那么会优先使用函数模板
    3. 可以通过加<>来强制使用函数模板
    
  4. 函数模板分文件编写

    1. 普通函数的声明放在头文件中,定义放在源文件中
    2. 函数模板只是函数的描述,没有实体,放在头文件中
    3. 具体化模板和普通函数一样
  5. 函数模板高级

    1. 如果decltype里面加括号了,那么就是引用类型
    2. 如果是左值,也是引用(通俗来讲,左值是指可以放在赋值运算符左侧的表达式,可以寻址,可被修改)
    3. 其他的都是和decltype里面的标识符类型一致。
    4. 如果里面是函数,函数名 => 指针类型,函数 => 函数的返回值类型
    decltype(a) b1;   // 和a相同
    
    decltype(func()) b2;  // 和函数返回值相同
    
    decltype(func) *b3 = func;  b3(); // b 是函数类型的指针, 可以当函数用
    
    decltype((func)) b4 = func; b4();  // 对函数名加了括号,那么b是函数的引用,也可以直接调用
    
    decltype((a)) b5 = x;  // 加了括号的标识符,那么b是引用
    
    decltype(++a) b6 = x;  // 表达式是左值,b也是引用
    
  6. 模板类的基本概念

    1. 模板类使用的时候必须指定数据类型
    2. 可以指定缺省值
    3. 模板类的函数也可以在类外定义,但是不能指定缺省值
    template <class T1, class T2 = string>  
    class AA {
    public:
    	T1 m_a;
    	T2 m_b;
    
    	AA() {}
    
    	AA(T1 a, T2 b) : m_a(a), m_b(b) {}
        
        T1 func();
    };
    
    
    template<class T1, class T2>
    T1 AA<T1, T2>::func() {
    	cout << m_a << endl;
    
    	return m_a;
    }
    
  7. 模板类的示例-栈

    1. delete [] 用于删除动态分配数组的内存(new动态分配)
    2. delete 用来删除动态分配的单个对象的内存
    template <class Datatype>
    class Stack {
    private:
    	Datatype* vv;
    	int size;
    	int top;
    public:
    	Stack() {}
    
    	Stack(int size_) : size(size_), top(0) {
    		vv = new Datatype[size_];
    	}
    
    	~Stack() {
    		delete[] vv;  // int 型用delete, string 型用delete[]
    		vv = nullptr;
    	}
    
    	bool isempty() {
    		return top == 0;
    	}
    
    	bool isfull() {
    		return top == size;
    	}
    
    	bool push(const Datatype& x) {
    		if (isfull()) {
    			cout << "栈满" << endl;
    			return false;
    		}
    		vv[top++] = x;
    		return true;
    	}
    
    	bool pop(Datatype& x) {
    		if (isempty()) {
    			cout << "栈空" << endl;
    			return false;
    		}
    		x = vv[--top];
    		return true;
    	}
    };
    
    int main() {
    	//Stack<int> st(5);
    	//st.push(1);
    	//st.push(2);
    	//st.push(3);
    	//st.push(4);
    	//st.push(5);
    	Stack<string> st(5);
    	st.push("hello");
    	st.push("king");
    
    	string item;
    	while (!st.isempty()) {
    		st.pop(item);
    		cout << item << endl;
    	}
    
    	return 0;
    }
    
  8. 模板类的示例-数组

    1. string是类,不能用memset初始化。memset会将字符数组的每个字节都设置为给定的值,可能会破坏string的内部结构
    template <class Datatype, int len> // 非通用数据类型用来指定数组大小
    class Array {  // 定长数组
    private:
    	Datatype* arr;
    public:
    	Array() {
    		arr = new Datatype[len];
    	}
    	~Array() {
    		delete[] arr;
    		arr = nullptr;
    	}
    
    	Datatype& operator[](int index) {
    		return arr[index];
    	}
    
    	const Datatype& operator[](const int& index) const {
    		return arr[index];
    	}
    };
    
    template <class T>
    class Vector {  // 变长数组
    private:
    	int len;
    	T* vec;
    public:
    	Vector(int size = 10) : len(size) {
    		vec = new T[size];
    	}
    
    	~Vector() {
    		delete[] vec;
    		vec = nullptr;
    	}
    
    	T& operator[](int index) {
    		return vec[index];
    	}
    
    	const T& operator[](int index) const {
    		return vec[index];
    	}
    
    	void resize(int size) {
    		if (size <= len) return;
    		T* newvec = new T[size];
    		for (int i = 0; i < len; ++i) newvec[i] = vec[i];
    		delete[] vec;
    		vec = newvec;
    		len = size;
    	}
    };
    
  9. 嵌套使用模板类

    template <class Datatype>
    class Stack {
    private:
    	Datatype* vv;
    	int size;
    	int top;
    public:
    	Stack(int size_ = 3) : size(size_), top(0) {
    		vv = new Datatype[size_];
    	}
    
    	~Stack() {
    		delete[] vv;
    		vv = nullptr;
    	}
    
    	Stack& operator=(const Stack& st) {
    		delete[] vv;
    		size = st.size;
    		top = st.top;
    		vv = new Datatype[size];
    		for (int i = 0; i < size; ++i) vv[i] = st.vv[i];
    		return *this;
    	}
    
    	bool isempty() {
    		return top == 0;
    	}
    
    	bool isfull() {
    		return top == size;
    	}
    
    	bool push(const Datatype& x) {
    		if (isfull()) {
    			cout << "栈满" << endl;
    			return false;
    		}
    		vv[top++] = x;
    		return true;
    	}
    
    	bool pop(Datatype& x) {
    		if (isempty()) {
    			cout << "栈空" << endl;
    			return false;
    		}
    		x = vv[--top];
    		return true;
    	}
    };
    
    
    template <class T>
    class Vector {
    private:
    	int len;
    	T* vec;
    public:
    	Vector(int size = 2) : len(size) {
    		vec = new T[size];
    	}
    
    	~Vector() {
    		delete[] vec;
    		vec = nullptr;
    	}
    
    	Vector& operator=(const Vector& v) {
    		delete[] vec;
    		len = v.len;
    		vec = new T[len];
    		for (int i = 0; i < len; ++i) vec[i] = v.vec[i];
    
    		return *this;
    	}
    
    	T& operator[](int index) {
    		if (index >= len) resize(index + 2);
    		return vec[index];
    	}
    
    	const T& operator[](int index) const {
    		return vec[index];
    	}
    
    	void resize(int size) {
    		if (size <= len) return;
    		T* newvec = new T[size];
    		for (int i = 0; i < len; ++i) newvec[i] = vec[i];  //注意这里,如果Vector内部的数据不是默认数据类型并且使用了堆区内存的话,会涉及到浅拷贝问题,此处内部是Stack,因此Stack要提供深拷贝,否则会出错
    		delete[] vec;
    		vec = newvec;
    		len = size;
    	}
    };
    
    
    int main() {
    	Vector<Stack<int>> vec;
    	vec[0].push(1); vec[0].push(2); vec[0].push(3);
    	vec[1].push(4); vec[1].push(5); vec[1].push(6);
    	vec[2].push(7); vec[2].push(9);
    
    	for (int i = 0; i < 3; ++i) {
    		int item;
    		while (vec[i].isempty() == false) {
    			vec[i].pop(item);
    			cout << item << " ";
    		}
    		cout << endl;
    	}
    
    	return 0;
    }
    
  10. 模板类具体化

    1. 优先级:完全具体化 > 部分具体化 > 普通模板类
    template <class T1, class T2>
    class A {
    public:
    	A() {
    		cout << "普通模板类" << endl;
    	}
    };
    
    template <class T1>
    class A<T1, string> {
    public:
    	A() {
    		cout << "部分具体化" << endl;
    	}
    };
    
    template <>
    class A<int, string> {
    public:
    	A() {
    		cout << "完全具体化" << endl;
    	}
    };
    
  11. 模板类与继承

    1. 派生类需要在初始化列表中指明基类的构造函数
    2. 模板类继承普通类
    class AA {
    public:
    	int c_;
    
    	AA(int c) : c_(c) {
    		cout << "调用了AA(int c) 构造函数" << endl;
    	}
    };
    
    template <class T1, class T2> 
    class BB : public AA {
    public:
    	T1 a_;
    	T2 b_;
    
    	BB(const T1 a, const T2 b, int c) : AA(c), a_(a), b_(b) {
    		cout << "调用了BB(const T1 a, const T2 b) 构造函数" << endl;
    	}
    };
    
    1. 普通类继承模板类的实例化版本
    template <class T1, class T2> 
    class BB {
    public:
    	T1 a_;
    	T2 b_;
    
    	BB(const T1 a, const T2 b) : a_(a), b_(b) {
    		cout << "调用了BB(const T1 a, const T2 b) 构造函数" << endl;
    	}
    };
    
    
    class AA : public BB<int, string> {
    public:
    	int c_;
    
    	AA(int c, const int a, const string b) : BB<int, string>(a, b), c_(c) {
    		cout << "调用了AA(int c, const int a, const string b) 构造函数" << endl;
    	}
    };
    
    1. 普通类继承模板类
    template <class T1, class T2> 
    class BB {
    public:
    	T1 a_;
    	T2 b_;
    
    	BB(const T1 a, const T2 b) : a_(a), b_(b) {
    		cout << "调用了BB(const T1 a, const T2 b) 构造函数" << endl;
    	}
    };
    
    template <class T1, class T2> 
    class AA : public BB<T1, T2> {
    public:
    	int c_;
    
    	AA(int c, const T1 a, const T2 b) : BB<T1, T2>(a, b), c_(c) {
    		cout << "调用了AA(int c, const T1 a, const T2 b) 构造函数" << endl;
    	}
    };
    
    1. 模板类继承模板类
    template <class T1, class T2> 
    class BB {
    public:
    	T1 a_;
    	T2 b_;
    
    	BB(const T1 a, const T2 b) : a_(a), b_(b) {
    		cout << "调用了BB(const T1 a, const T2 b) 构造函数" << endl;
    	}
    };
    
    template <class T, class T1, class T2>
    class CC : public BB<T1, T2> {
    public:
    	int d_;
    
    	CC(int d, T1 a, T2 b) : BB<T1, T2>(a, b), d_(d) {
    		cout << "调用了 CC(int d) 构造函数" << endl;
    	}
    };
    
    1. 模板类继承模板参数给出的基类
    template <class T>
    class AA {
    public:
    	AA() {
    		cout << "调用了AA()构造函数" << endl;
    	}
    };
    
    template <class T1, class T2> 
    class BB {
    public:
    	T1 a_;
    	T2 b_;
    
    	BB() {
    		cout << "调用了BB() 构造函数" << endl;
    	}
    
    	BB(T1 a, T2 b) : a_(a), b_(b) {
    		cout << "调用了BB(T1 a, T2 b)构造函数" << endl;
    	}
    };
    
    
    template <class T>
    class DD : public T{
    public:
    	DD() {
    		cout << "调用了DD()构造函数" << endl;
    	}
    	DD(int a, string b) : T(a, b) {
    		cout << "调用了DD(int a, string b) 构造函数" << endl;
    	}
    };
    
  12. 模板类与函数

    // 普通函数,参数和返回值是模板类的一个实例化版本
    AA<int, string> func(AA<int, string>& aa) {
    	aa.show();
    	cout << "调用了AA<int, string> func" << endl;
    	return aa;
    }
    
    // 适用于AA类的函数模板
    template <typename T1, typename T2>
    AA<T1, T2> func(AA<T1, T2>& aa) {
    	aa.show();
    	cout << "调用了AA<T1, T2> func" << endl;
    	return aa;
    }
    
    // 通用函数模板
    template <typename T>
    T func(T& t) {
    	t.show();
    	cout << "调用了T func()" << endl;
    	return t;
    }
    
  13. 模板类与友元

    1. 非模板友元
    template <class T1, class T2>
    class AA {
    	friend void func(AA<int, string>& a);
    	friend void func(AA<int, int>& a);
    private:
    	T1 a_;
    	T2 b_;
    
    public:
    	AA(T1 a, T2 b) : a_(a), b_(b) {}
    
    	void show() {
    		cout << "a_ = " << a_ << ", b_ = " << b_ << endl;
    	}
    };
    
    
    void func(AA<int,string>& a) {
    	cout << "func: aa = " << a.a_ << ", b_ = " << a.b_ << endl;
    }
    
    void func(AA<int, int>& a) {
    	cout << "func: aa = " << a.a_ << ", b_ = " << a.b_ << endl;
    }
    
    1. 约束模板友元
    template <typename T>        // 第一步 声明。可用于多个模板类
    void func(T& t);
    
    template <class T1, class T2>
    class AA {
    	friend void func<>(AA<T1, T2>& t);  // 第二步
    private:
    	T1 a_;
    	T2 b_;
    
    public:
    	AA(T1 a, T2 b) : a_(a), b_(b) {}
    
    	void show() {
    		cout << "a_ = " << a_ << ", b_ = " << b_ << endl;
    	}
    };
    
    template <class T1, class T2> 
    class BB {
    	friend void func<>(BB<T1, T2>& t); 
    private:
    	T1 a_;
    	T2 b_;
    
    public:
    	BB(T1 a, T2 b) : a_(a), b_(b) {}
    
    	void show() {
    		cout << "a_ = " << a_ << ", b_ = " << b_ << endl;
    	}
    };
    
    template <typename T>   //  第三步  通用类型
    void func(T& t) {
    	cout << "通用 func: a_ = " << t.a_ << ", b_ = " << t.b_ << endl;
    }
    
    template <>   // 第三步  具体化
    void func(AA<int, string>& t) {
    	cout << "具体化 func: a_ = " << t.a_ << ", b_ = " << t.b_ << endl;
    }
    
  14. 模板类的成员模板

    template <class T1, class T2>
    class AA {
    public:
    	T1 a_;
    	T2 b_;
    
    	AA(T1 a, T2 b) : a_(a), b_(b) {};
    	void show() {
    		cout << "AA a_ = " << a_ << ", AA b_ = " << b_ << endl;
    	}
    
    	template <class T>   // 嵌套类模板
    	class BB {
    	public:
    		T x_;
    		T1 y_;
    
    		BB() {}
    		BB(T x, T1 y) : x_(x), y_(y) {}
    		
    		void show() {
    			cout << "BB x_ = " << x_ << ", y_ = " << y_ << endl;
    		}
    	};
    	BB<int> bb;
    
    	template <typename T>   // 嵌套模板函数
    	void show(T& t) {
    		cout << "t = " << t << endl;
    		show();
    		bb.show();
    	}
    };
    
    
    int main() {
    
    	AA<int, string> a(1, "mary");
    	a.bb.x_ = 1;
    	a.bb.y_ = 2;
    	string str = "hello mary";
    	a.show(str);
    
    	return 0;
    }
    
    
  15. 将模板类用作参数

    template <class T, int len>
    class LinkList {
    	T* head_;
    	int len_ = len;
    public:
    	void insert() {
    		cout << "向链表中插入一个元素" << endl;
    	}
    
    	void Delete() {
    		cout << "在链表中删除一个元素" << endl;
    	}
    
    	void modify() {
    		cout << "修改链表中的一个元素" << endl;
    	}
    };
    
    
    template <class T, int len>
    class Array {
    	T* arr;
    	int len_ = len;
    public:
    	void insert() {
    		cout << "向数组中插入一个元素" << endl;
    	}
    
    	void Delete() {
    		cout << "在数组中删除一个元素" << endl;
    	}
    
    	void modify() {
    		cout << "修改数组中的一个元素" << endl;
    	}
    
    };
    
    
    template <template<class, int>class T, class T1, int len>
    class XX {
    public:
    	T<T1, len> table;
    
    	void insert() {
    		table.insert();
    	}
    
    	void Delete() {
    		table.Delete();
    	}
    
    	void modify() {
    		table.modify();
    	}
    };