|
文章目录
泛型编程
当我们实现一个通用的swap函数时,可能会这样写
<p><pre> <code class="prism language-cpp"><span class="token keyword">void</span> <span class="token function">Swap</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token operator">&</span> left<span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token operator">&</span> right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">int</span> temp <span class="token operator">=</span> left<span class="token punctuation">;</span>
left <span class="token operator">=</span> right<span class="token punctuation">;</span>
right <span class="token operator">=</span> temp<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">Swap</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token operator">&</span> left<span class="token punctuation">,</span> <span class="token keyword">double</span><span class="token operator">&</span> right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">double</span> temp <span class="token operator">=</span> left<span class="token punctuation">;</span>
left <span class="token operator">=</span> right<span class="token punctuation">;</span>
right <span class="token operator">=</span> temp<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">Swap</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token operator">&</span> left<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">&</span> right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">char</span> temp <span class="token operator">=</span> left<span class="token punctuation">;</span>
left <span class="token operator">=</span> right<span class="token punctuation">;</span>
right <span class="token operator">=</span> temp<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
</code></pre></p>
虽然可以通过函数重载来实现,但也有几个缺点:
如果出现新类型,则需要根据类型重写函数的重载;如果一个重载出现错误,可能会导致其他重载出现问题。
如果我们不按照上面的方法写泛目录交易,而是写一个模板,然后根据类型推导出不同的函数,那么问题就迎刃而解了。
泛型编程就是这样一个想法:编写独立于类型的泛型代码是代码重用的一种手段。模板是泛型编程的基础。
功能模板
概念:函数模板表示一系列函数,这些函数在使用时与类型无关、参数化,并根据参数的类型生成特定于类型的函数版本。
格式:模板或模板
原则:函数模板是一个蓝图,而不是函数本身。编译器根据传递的参数的类型生成一个特定的函数。
在编译器的编译阶段,对于模板函数的使用,编译器需要根据传入参数的类型,推导出相应类型的函数进行调用。
如上图,int类型使用函数模板时,模板根据传入实参的int类型推导出int类型的swap。
实例化:
当函数模板与不同类型的参数一起使用时泛目录交易,称为函数模板的实例化。模板实例化分为:隐式实例化和显式实例化。
隐式实例化:编译器根据实际参数的类型推断模板参数的类型。就像上图中的模板,根据三类参数推导出三个函数。
但无法成功推导出如下函数。
<p><pre> <code class="prism language-c++">template<class T>
void Swap(T& x, T& y){
T tmp = x;
x = y;
y = tmp;
}
int main(){
int x = 10;
char y = 'a';
Swap(x, y);
return 0;
}
</code></pre></p>
因为,在使用模板根据实参推导函数的时候,我们的实参类型分别是int和double,但是我们只用到了一个T,而这个T只能推导出一种类型,那么T是int还是double呢?因此,无法成功推演。
解决方案是:
自己施展 Swap((char)x, y); 或交换(x,(int)y);使用显式实例化
显式实例化:在函数名后面指定推导的类型
<p><pre> <code class="prism language-c">template<span class="token operator"><</span>class T<span class="token operator">></span>
<span class="token keyword">void</span> <span class="token function">Swap</span><span class="token punctuation">(</span>T<span class="token operator">&</span> x<span class="token punctuation">,</span> T<span class="token operator">&</span> y<span class="token punctuation">)</span><span class="token punctuation">{</span>
T tmp <span class="token operator">=</span> x<span class="token punctuation">;</span>
x <span class="token operator">=</span> y<span class="token punctuation">;</span>
y <span class="token operator">=</span> tmp<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">int</span> x <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span>
<span class="token keyword">char</span> y <span class="token operator">=</span> <span class="token char">'a'</span><span class="token punctuation">;</span>
Swap<span class="token operator"><</span><span class="token keyword">int</span><span class="token operator">></span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
Swap<span class="token operator"><</span><span class="token keyword">char</span><span class="token operator">></span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></p>
模板参数的匹配原则
非模板函数可以与同名函数模板共存,函数模板可以实例化为非模板函数。
<p><pre> <code class="prism language-cpp"><span class="token comment">// 专门处理int的加法函数</span>
<span class="token keyword">int</span> <span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">int</span> left<span class="token punctuation">,</span> <span class="token keyword">int</span> right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">return</span> left <span class="token operator">+</span> right<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 通用加法函数</span>
<span class="token keyword">template</span><span class="token operator"><</span><span class="token keyword">class</span> <span class="token class-name">T</span><span class="token operator">></span>
T <span class="token function">Add</span><span class="token punctuation">(</span>T left<span class="token punctuation">,</span> T right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">return</span> left <span class="token operator">+</span> right<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">Test</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 与非模板函数匹配,编译器不需要特化</span>
<span class="token generic-function"><span class="token function">Add</span><span class="token generic class-name"><span class="token operator"><</span><span class="token keyword">int</span><span class="token operator">></span></span></span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 调用编译器特化的Add版本</span>
<span class="token punctuation">}</span>
</code></pre></p>
对于非模板函数和同名的函数模板,如果其他条件相同,编译器在调用函数时会优先选择非模板函数,这样模板就不会被实例化。如果模板可以推导出匹配更好的函数,则选择该模板。
<p><pre> <code class="prism language-cpp"><span class="token comment">// 专门处理int的加法函数</span>
<span class="token keyword">int</span> <span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">int</span> left<span class="token punctuation">,</span> <span class="token keyword">int</span> right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">return</span> left <span class="token operator">+</span> right<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 通用加法函数</span>
<span class="token keyword">template</span><span class="token operator"><</span><span class="token keyword">class</span> <span class="token class-name">T1</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">T2</span><span class="token operator">></span>
T1 <span class="token function">Add</span><span class="token punctuation">(</span>T1 left<span class="token punctuation">,</span> T2 right<span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">return</span> left <span class="token operator">+</span> right<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">Test</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 与非函数模板类型完全匹配,不需要函数模板实例化</span>
<span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数</span>
<span class="token punctuation">}</span>
</code></pre></p>
模板函数不允许自动类型转换,但普通函数可以。
类模板
格式:
<p><pre> <code class="prism language-cpp"><span class="token keyword">template</span><span class="token operator"><</span><span class="token keyword">class</span> <span class="token class-name">T1</span><span class="token punctuation">,</span> <span class="token keyword">class</span> <span class="token class-name">T2</span><span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token operator">></span>
<span class="token keyword">class</span> 类模版名<span class="token punctuation">{</span>
<span class="token comment">//类内成员定义</span>
<span class="token punctuation">}</span>
</code></pre></p>
为最简单的堆栈编写模板
<p><pre> <code class="prism language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><iostream></span></span>
<span class="token keyword">template</span><span class="token operator"><</span><span class="token keyword">class</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">class</span> <span class="token class-name">Stack</span><span class="token punctuation">{</span>
<span class="token keyword">public</span><span class="token operator">:</span>
<span class="token keyword">explicit</span> <span class="token function">Stack</span><span class="token punctuation">(</span>size_t capacity <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">)</span>
<span class="token operator">:</span><span class="token function">_array</span><span class="token punctuation">(</span><span class="token keyword">nullptr</span><span class="token punctuation">)</span>
<span class="token punctuation">,</span><span class="token function">_top</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">,</span><span class="token function">_capacity</span><span class="token punctuation">(</span>capacity<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
_array <span class="token operator">=</span> <span class="token punctuation">(</span>T<span class="token operator">*</span><span class="token punctuation">)</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span>T<span class="token punctuation">)</span> <span class="token operator">*</span> _capacity<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>_array <span class="token operator">==</span> <span class="token keyword">nullptr</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token function">perror</span><span class="token punctuation">(</span><span class="token string">"malloc fail"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">exit</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator">~</span><span class="token function">Stack</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">delete</span> _array<span class="token punctuation">;</span>
_top <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
_capacity <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">Push</span><span class="token punctuation">(</span>T x<span class="token punctuation">)</span><span class="token punctuation">{</span>
_array<span class="token punctuation">[</span>_top<span class="token operator">++</span><span class="token punctuation">]</span> <span class="token operator">=</span> x<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span><span class="token operator">:</span>
T<span class="token operator">*</span> _array<span class="token punctuation">;</span>
size_t _top<span class="token punctuation">;</span>
size_t _capacity<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
Stack<span class="token operator"><</span><span class="token keyword">int</span><span class="token operator">></span> s1<span class="token punctuation">;</span>
Stack<span class="token operator"><</span><span class="token keyword">char</span><span class="token operator">></span> s2<span class="token punctuation">;</span>
Stack<span class="token operator"><</span><span class="token keyword">double</span><span class="token operator">></span> s3<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></p>
类模板只能显式实例化,指定类型放在类名之后。
如果成员函数定义在类外,需要带模板参数列表
<p><pre> <code class="prism language-cpp"><span class="token keyword">template</span><span class="token operator"><</span><span class="token keyword">class</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">class</span> <span class="token class-name">Stack</span><span class="token punctuation">{</span>
<span class="token keyword">public</span><span class="token operator">:</span>
<span class="token keyword">explicit</span> <span class="token function">Stack</span><span class="token punctuation">(</span>size_t capacity <span class="token operator">=</span> <span class="token number">4</span><span class="token punctuation">)</span>
<span class="token operator">:</span><span class="token function">_array</span><span class="token punctuation">(</span><span class="token keyword">nullptr</span><span class="token punctuation">)</span>
<span class="token punctuation">,</span><span class="token function">_top</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">,</span><span class="token function">_capacity</span><span class="token punctuation">(</span>capacity<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
_array <span class="token operator">=</span> <span class="token punctuation">(</span>T<span class="token operator">*</span><span class="token punctuation">)</span> <span class="token function">malloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span>T<span class="token punctuation">)</span> <span class="token operator">*</span> _capacity<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>_array <span class="token operator">==</span> <span class="token keyword">nullptr</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token function">perror</span><span class="token punctuation">(</span><span class="token string">"malloc fail"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">exit</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator">~</span><span class="token function">Stack</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
<span class="token keyword">delete</span> _array<span class="token punctuation">;</span>
_top <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
_capacity <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">Push</span><span class="token punctuation">(</span>T x<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">private</span><span class="token operator">:</span>
T<span class="token operator">*</span> _array<span class="token punctuation">;</span>
size_t _top<span class="token punctuation">;</span>
size_t _capacity<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">//类外定义,需要带上模版参数列表</span>
<span class="token keyword">template</span><span class="token operator"><</span><span class="token keyword">class</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">void</span> <span class="token function">Push</span><span class="token punctuation">(</span>T x<span class="token punctuation">)</span><span class="token punctuation">{</span>
_array<span class="token punctuation">[</span>_top<span class="token operator">++</span><span class="token punctuation">]</span> <span class="token operator">=</span> x<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></p>
类模板与函数模板相同。类模板也不是真正的类,而是蓝图。用类模板实例化的结果是一个真正的类。
豪侠泛目录站群程序,专业泛目录,站群,二级目录,泛站群程序! |
|