#include <iostream>
using namespace std;
template<typename T> // template 是声明模板 <typename T> 是类型 也可以写成<class T> 都是一样的
class Vector {
public: // 普通类的构造函数都是 类名() 但是模板类就必须Vector<type>()了
explicit Vector<T>(int cap = 5); // 体类定义就跟普通的构造函数一样 但是还是要Vector<type>()开始 因为T才是完整型
~Vector<T>() { delete[] date; }; //{ delete[]date; } // 体类声明QWQ
T& operator[](size_t index);
const T& operator[](size_t index)const;
Vector<T>(const Vector<T>& array);
Vector<T>& operator=(const Vector<T>& rhs);
bool push_back(const T &e);
size_t size()const { return n; }
private:
T* date{ nullptr };
size_t capacity{ 0 }; //动态空间的大小
size_t n{ 0 }; // 实际的数据元素个数
};
template<typename T> // 体外声明必须要带模板 因为Vector是模板类 类跟模板类的不同就是泛型 T就是传递的类型其他基本一样
Vector<T>::Vector(int cap) :date{ new T[cap] }, n{ 0 }{
if (date) {
capacity = cap;
}
}
template<typename T>
Vector<T>::Vector(const Vector& vec) :Vector{ vec.capacity } { // 在这里先调用构造函数在 拷贝构造函数模板
if (!date)
return;
n = vec.n;
for (size_t i = 0; i < n; i++) { // 拷贝值 传递过来的是老对象的值 赋给新对象值 this
date[i] = vec.date[i];
}
}
template<typename T>
T& Vector<T>::operator[](size_t index) { // 下标重载运算符模板 可以调用const版本
if (index >= n)
throw"下标非法";
return date[index];
}
template<typename T>
const T& Vector<T>::operator[](size_t index)const { // const 重载 注意之前说到的const_cast<T&>a 可以出去const形 这样就可以调用非const上面的同样模板下标函数了
if (index >= n) // 还有std::as_const 在头文件<utility> 作用可以将一个非const对象强制转换成const const_cast<T&>a 是移除const性
throw"下标非法"; // 这个就调用不了非const版本 解决方法如下
return date[index];
}
template<typename T>
T& Vector<T>::operator[](size_t index){
return const_cast<T&>(std:: as_const(*this)[index]); // 将有const性 转为无const性
}
template<typename T>
Vector<T>& Vector<T>::operator=(const Vector& rhs) { //rhs 是a 前面&是b
if (&rhs != this) { // 数据不同
Vector<T> ret(rhs); // 调用拷贝函数 让ret值都更新
T* temp = ret.date; // 当然交换也可以用标准库 std::swap(data,ret.data);
ret.date = date; // 更新本数据this
date = temp;
n = ret.n;
capacity = ret.capacity;
}
return *this; // 返回对象引用
}
template<typename T>
bool Vector<T>::push_back(const T&e) {
if (capacity == n) { //空间已满
T* temp = new T[2 * capacity];
if (!temp)
return false;
capacity *= 2;
for (size_t i{}; i < n; ++i)
temp[i] = date[i]; //吧date数据赋给temp 指针就是数组
delete[] date; // 删除date
date = temp; // 吧扩充了内存赋给date
}
date[n] = e;
n++;
return true;
}
template<>
class X<const char*>{ // 模板类的专门化 处理特定类型的 const char* 类型的类
} // 如果一个模板类里面所有的模板参数都是特定类型的 那么这个专门化实际上是一个类 而不是模板 当然这个类的完整名是X<const char*> 这种模板类称为完全专门类
template<int s = 10> // 这个参数是默认值
class XX<const char*,s>{ // 还有一种是部分专门化 就是专门化部分参数 仍然是一个模板类
}
template<typename T,int s> // 这个s必须出现在T后面
class XX<T* ,s>{ // 将类模板参数专门化 T* 还要注意专门化模板类不能有默认值
} // 在实例化时编译器会根据传递的值选一个最佳的匹配模板类
//如果X<const char*,3>x 会 匹配到template<int s = 10> class XX<const char*,s> 匹配模板
int main() {
Vector<int>a;
for (auto i = 0; i <= 6; i++)
a.push_back(2 * i + 1); // 添加数据
a[3] = 30;
for (auto i = 0; i != a.size(); i++)
cout << a[i] << '\t';
cout << endl;
Vector<int>b; // 如果是同类型就刚才的类创造了一个对象b 如果是新类型就重新构造一个
b = a; // 调用赋值运算符 并且把a值赋给b 换成double 会导致类型不匹配
for (auto i = 0; i != a.size(); i++)
cout << b[i] << '\t';
while (true);
return 0;
}
下面是模板友元类的用法
#include <iostream>
template<typename T>
class X {
public:
X() = default;
friend void fun(); // 和普通的友元一样
friend class A; // 友元类
private:
int a{ 0 };
};
class A {
public:
A() = default;
};
void fun() {
X<int>x;
X<double>y; // 如果没有 x y对象 就可以用 A对象友元来访问私有成员<< a.a
X<A>a; //a.a;
std::cout << a.a << '\t'<<x.a<<'\t'<<y.a<<'\n';
}
// 假如模板类里面有一个友元函数模板 使用的是类模板参数如
//template<typename T>
//class Y {
// friend Y<T>* g(Y<T>& e); // 这个是一个类模板 友元函数
//}; 列如下
template<typename T>
class Point{
public:
T x, y;
Point(const T x, const T y) :x(x), y(y){}
friend Point<T>* g(Point<T>& e); // 这个是一个类模板 友元函数
};
template<typename T>
Point<T>* g(Point<T>& e) { //Point<T> 只是声明模板类的方法 实际类型还是Point<T>是对象 如果换成函数就是T
Point<T>* p = new Point<T>(e); //可以看成Point<T> *p = new Point<T>(e); 调用构造函数
return p; // 返回对象 类型是一个类
}
int main() {
fun();
Point<int>e(3,4);
Point<int>* p = g<int>(e);
std::cout << p->x << "\t" << p->y << "\n";
Point<double>x(2, 3);
auto q = g<double>(x); //auto q = g<int>(x); 会报错 g <int>不是Point<double>友元 就是类型不一样
std::cout << q->x << "\t" << q->y << "\n"; // 因为模板类是什么类型 类函数就是什么类型 又没特殊定义
while (true);
return 0;
}