类模板的局部特化
类模板可以定义多个类型参数
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T1, typename T2>
class Test
{
public:
void add(T1 a, T2 b)
{
cout<<(a + b)<<endl;
}
};
int main(int argc, char *argv[])
{
Test<double, int> t;
t.add(10.0001, 8);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
18.0001
Press the enter key to continue ...
类模板可以被局部特化
想要为类模板指定特定的实现,并希望某些类型参数仍然由模板的用户来指定
优先选择局部特化的类模板
在匹配时,优先匹配局部特化的类模板,如果同时有两个局部特化的类模板满足条件,则会让编译器进入选择困难状态,然后报错捏
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T1, typename T2>
class Test
{
public:
void add(T1 a, T2 b)
{
cout<<(a + b)<<endl;
}
};
/*
template<typename T>
class Test<T, T>
{
public:
void add(T a, T b)
{
cout<<"add(T a, T b)"<<endl;
cout<<static_cast<T>(a + b)<<endl;
}
};
*/
template<typename T>
class Test<T, int>
{
public:
void add(T a, int b)
{
cout<<"add(T a, int b)"<<endl;
cout<<a + b<<endl;
}
};
template<typename T1, typename T2>
class Test<T1*, T2*>
{
public:
void add(T1* a, T2* b)
{
cout<<"add(T1* a, T2* b)"<<endl;
}
};
int main(int argc, char *argv[])
{
int i = 0;
int j = 0;
Test<double, int> t; // <T, int>
Test<long, long> ti; // <T1, T2>
Test<float, int> tt; // <T, int>
Test<int*, int*> tp; // <T*, T*>
//ps:当可以同等匹配两个局部特化时,就会出现选择困难,报错
t.add(10.0001, 8);
ti.add(2, 3);
tt.add(4, 5);
tp.add(&i, &j);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
add(T a, int b)
18.0001
5
add(T a, int b)
9
add(T1* a, T2* b)
Press the enter key to continue ...
为什么需要特化,而不重新定义新类?
-
特化和重新定义新类看上去没有本质区别,但是如果定义新类,那么将变成一个类模板和一个新类,使用的时候需要考虑究竟是用类模板还是用新类
-
而特化可以统一的方式使用类模板和特化类,编译器自动优先选择特化类
(考虑友好度)
非类型模板参数
函数模板和类模板的模板参数可以是普通数值
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T, int N>
void func()
{
T array[N] = {0};
for(int i = 0; i < N; i++)
{
array[i] = i + 1;
cout<<array[i]<<" ";
}
cout<<endl;
}
int main(int argc, char *argv[])
{
func<int, 5>();
func<float, 10>();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
Press the enter key to continue ...
非类型模板参数的限制
-
变量不能作为模板参数
-
浮点数(现在可以)和类对象不能作为模板参数
-
全局指针不能作为模板参数
整型作为模板参数是最安全可靠的
非类型模板参数与特化
(让编译器在给我们做递归的计算)
#include <cstdlib>
#include <iostream>
using namespace std;
template<int N>//把sum变成类模板
class Sum
{
public:
//static const int s = 1;在c++中,只有static const能赋一个初值
static const int VALUE = Sum<N - 1>::VALUE + N;//求1+...+N;
};
template< >//特化类,告诉递归何时结束,当Sum<1>的时候就不要递归了
class Sum<1>
{
public:
static const int VALUE = 1;
};
int main(int argc, char *argv[])
{
//VALUE是个常量,Sum<10>是个类,通过类名访问一个static的成员常量
cout<<Sum<10>::VALUE<<endl;//编译器编译的时候就把这件事情做完了,运行的时候没有时间开销
cout<<Sum<100>::VALUE<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result:
55
5050
Press the enter key to continue ...
工程问题
在实际工程中内存操作是bug的重要来源
C++将堆内存交由程序员自由使用,因此
-
未及时释放,将产生内存泄漏
-
重复释放同一段内存,行为未知
-
使用越界,操作了不属于自己的内存
怎样最大限度的避开上述的使用问题?
内存越界的问题常发生于数组的使用中
解决方案:数组类
工程中,在非特殊情况下,要求开发者使用预先编写的数组类对象代替C语言中的原生数组
内存泄漏和内存多次释放常发生于指针的使用过程中
解决方案:智能指针
工程中,要求开发者使用预先编写的智能指针类对象代替C语言中的原生指针
智能指针
工程中的智能指针是一个类模板
-
通过构造函数接管申请的堆内存
-
通过析构函数确保堆内存被及时释放
-
通过重载指针运算符 *和->模拟指针的行为
-
通过重载比较运算符==和 != 模拟指针的比较
智能指针的创建与使用
SmartPointer.h
#ifndef _SMARTPOINTER_H_
#define _SMARTPOINTER_H_
template<typename T>
class SmartPointer
{
protected:
T* m_pointer;
public:
SmartPointer();
SmartPointer(const T* pointer);//接管申请的堆内存
~SmartPointer();//确保堆内存被及时释放
T* operator->();//返回一个T*类型,T*指针???
T& operator*();//返回一个T&类型,T&地址???
};
#endif
SmartPointer.hpp
#ifndef _SMARTPOINTER_DEF_H_
#define _SMARTPOINTER_DEF_H_
#include "SmartPointer.h"
template<typename T>
SmartPointer<T>::SmartPointer()
{
m_pointer = NULL;
}
template<typename T>
SmartPointer<T>::SmartPointer(const T* pointer)
{
m_pointer = const_cast<T*>(pointer);//类型转换,去掉const
}
template<typename T>
SmartPointer<T>::~SmartPointer()
{
delete m_pointer;
}
template<typename T>
T* SmartPointer<T>::operator->()
{
return m_pointer;
}
template<typename T>
T& SmartPointer<T>::operator*()
{
return *m_pointer;
}
#endif
main.cpp
#include <cstdlib>
#include <iostream>
#include "SmartPointer.hpp"
using namespace std;
class Test
{
public:
int i;
void print()
{
cout<<i<<endl;
}
};
int main(int argc, char *argv[])
{
//SmartPointer<int>pi(new int(5));
SmartPointer<int> pi = new int(5);//开始时运行构造,整个程序运行完毕运行析构
SmartPointer<Test> pt = new Test();
cout<<*pi<<endl;
*pi = 10;
cout<<*pi<<endl;
pt->i = 20;
pt->print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
result
5
10
20
Press the enter key to continue ...