靜多態

template template template

概述

靜多態(Static Polymorphism)是多態性的一種。多態性可按照發生的時間段分為靜多態和動多態(Dynamic Polymorphism)。其中靜多態就是綁定發生在編譯期(compile-time),此種綁定稱為靜態綁定static-binding);而動多態就是綁定發生在運行期(run-time),此種綁定稱為動態綁定(dynamic-binding)。

具體分類

1)非參數化多態(Ad-hoc polymorphism):
a)函式重載(Function Overloading)
b)運算符重載(Operator Overloading)
2)參數化多態(Parametric polymorphism)
c)模板(Template)
其實非參數化多態和參數化多態並不衝突,而且相輔相成,它們混合使用能夠帶來更大的靈活性,函式模板重載就是很好的例子:
template<typename T>
T max(const T& lhs, const T& rhs)
{
return lhs > rhs ? lhs : rhs;
}
template<typename T>
T max(const T& fst, const T& sec, const T& thd)
{
return max(max(fst, sec), thd);
}
使用:
max(1, 3, 5);
max(2.4, 4.2);

不同編程範例中的表現形式

註:由於C++同時支持下面要提到的三種編程範例(Programming Paradigm) ,所以就用C++語言為例來說明

過程化編程

C++中對靜多態的支持方式有:全局函式重載和運算符重載。
全局函式重載代碼:
一下代碼中均不考慮函式的返回類型,因為C++中不允許只有函式類型不同的重載。
1)參數個數不同而參數類型相同(對應位置):
void defParamNum(int paramFst);
void defParamNum(int paramFst, double paramSec);
2)參數個數相同而參數類型不同:
void defParamType (int paramFst, double paramSec);
void defParamType (double paramFst, int paramSec);
3)參數個數和參數類型都不相同:
void defBoth(int paramFst);
void defBoth(double paramFst, int paramSec);
運算符重載代碼:
其實運算符重載也是一種函式重載。其中多了一些限制,比如一元運算符、二元運算符所要求的參數個數,還有運算符重載的元型(Prototype)都有明確的規定,再就是一些像C語言等不支持運算符重載,所以這裡單獨列出來。
一元運算符重載:(負值運算符 operator -)
Complex operator – (const Complex& elem)
{
// 複數temp的值為0 + 0i
Complex temp;
// 將temp的實部設為elem實部負數
temp.setReal(-elem.getReal());
// 將temp的虛部設為elem虛部的負數
temp.setImage(-elem.getImage());
// 返回temp複數,此時temp就是elem對應的負數了
return temp;
}
二元運算符重載:(加法運算符 operator +)
Complex operator + (const Complex& lhs, const Complex& rhs)
{
// 複數temp的值為0 + 0i
Complex temp;
// 將temp的實部設為lhs和rhs實部的和
temp.setReal(lsh.getReal() + rhs.getReal());
// 將temp的虛部設為lhs和rhs虛部的和
temp.setImage(lsh.getImage() + rhs.getImage());
// 返回temp複數,此時的temp就是lhs和rhs的和
return temp;
}

面相對像編程

其實面向對象編程(Object-Oriented Programming)中也表現為函式重載和運算符重載。
函式重載:成員函式重載,靜態成員函式(static)重載,虛函式重載,友元函式重載。
class Complex {
public:
// 構造函式重載:
Complex() : m_real(0), m_image(0) { };
Complex(double real, double image) : m_real(real), m_image(image) { };
// 靜態成員函式重載:不恰當的例子
staticvoid staticFunc()
{
std::cout << "staticFunc()" << std::endl;
}
staticvoid staticFunc(int oneParam)
{
std::cout << "staticFunc(int oneParam)" << std::endl;
}
// 虛函式重載:不恰當的例子
virtualvoid virtualFunc()
{
std::cout << "virtualFunc()" << std::endl;
}
virtualvoid virtualFunc(int oneParam)
{
std::cout << "virtualFunc(int oneParam)" << std::endl;
}
// 虛函式重載:不恰當的例子!其友元函式就是一般的全局函式
friendvoid friendFunc();
friendvoid friendFunc(int oneParam);
// 運算符重載:Comple + Comple
// Complex + Complex成員版本:允許一個complex對象和另一個Complex對象相加
Complex operator + (const Complex& elem)
{
return Complex(m_real + elem.m_real, m_image + elem.m_image);
}
// Complex + double成員版本:允許一個complex對象和另一個double對象相加
// 只能是Complex + double,不能是double + Complex
Complex operator + (double elem)
{
return Complex(m_real + elem, m_image);
}
// Complex + Complex友元版本:允許一個complex對象和另一個Complex對象相加
friend Complex operator + (const Complex& lsh, const Complex& rhs)
{
return Complex(lsh.m_real + rhs.m_real, lsh.m_image + rhs.m_image);
}
// Complex + double友元版本:允許一個complex對象和另一個double對象相加
// 只能是Complex + double,不能是double + Complex
friend Complex operator + (const Complex& lsh, double rhs)
{
return Complex(lsh.m_real + rhs, lsh.m_image);
}
// double + Complex友元版本:允許一個double對象和另一個Complex對象相加
//只能是double + Complex,不能是Complex + double
//和上面的Complex + double友元版本相輔相成
friend Complex operator + (double lhs, const Complex& rhs)
{
return Complex(lhs + rhs.m_real, rhs.m_image);
}
private:
double m_real;
double m_image;
};
void friendFunc()
{
std::cout << "virtualFunc()" << std::endl;
}
void friendFunc(int oneParam)
{
std::cout << "virtualFunc(int oneParam)" << std::endl;
}
運算符重載:運算符成員式重載,運算符友元式重載。
註:見Complex類定義中的運算符重載部分!

泛型編程

在C++中,泛型編程(Generic Programming) 是通關過模板來實現的,然而模板不是與上述兩種編程範例有所不同,它必須依附於上述的某種範例,在某範例的基礎上來實現,就像面向對象和過程化編程的關係一樣。下面就是模板分類:
按泛化對象可分為:
1)類型泛化(Type):
template <typename T>
class List {
// ...
};
List<int> iList; // iList是能夠存儲int類型的鍊表對象
2)數值泛化(Value):
template <unsigned Num>
class Bit {
// ...
};
Bit<3> bit3; // bit3是長度為3位的位對象
3)數值和類型泛化(Type & Value):
template <typename T, unsigned Num>
class Array {
// ...
};
Array<int, 3> iArray3; // iArray3是能夠存儲3個int類型的數組對象
按泛化的載體可分為:
函式模板
template <typename>
void functionGeneric()
{
// ...
}
類模板
template <typename>
class classGeneric {
// ...
};

靜多態與動多態

優缺點比較

靜多態是以犧牲靈活性而獲得運行速度的一種做法;而動多態則恰恰相反,它是以犧牲運行速度而獲取靈活性的做法。當然這么說是不全面的,看看下面這個特殊的套用:

使用靜多態來實現動多態

這是一種在模板元編程(Template Metaprogramming)中常見的標準編程技巧。在C++中,可以藉助模板來實現面向對象語言所支持動多態相似的功能特性(C++中指的就是的virtual 函式)。
下面是C++本身所支持多態形式:(virtual版)
#include <iostream>
class Base {
public:
virtual void method() = 0;
virtual ~Base() { }
};
class Derived : public Base {
public:
virtual void method()
{
std::cout << "Derived" << std::endl;
}
};
class Derived2 : public Base {
public:
virtual void method()
{
std::cout << "Derived2" << std::endl;
}
};
int main()
{ Base *pBase = new Derived;
pBase->method(); // 輸出:"Derived"
delete pBase;
Base *pBase2 = new Derived2;
pBase2->method(); // 輸出:"Derived2"
delete pBase2;
return 0;
}
註:C++本身是藉助virtual關鍵字來實現多態的(dynamic polymorphism),而通常編譯器是藉助virtual look-up tables(虛函式表)來決定該調用那個版本的函式,當然這一過程發生在運行期。
下面是使用CRTP(Curiously Recurring Template Pattern)來實現多與上面對應功能的靜多態代碼:
#include <iostream>
template <class Derived>
class Base {
public:
void method()
{
// ...
static_cast<Derived*>(this)->implementation(); 
// ...
}
};
class Derived : private Base<Derived> {
public:
void implementation()
{
std::cout << "Derived" << std::endl;
}
};
class Derived2 : private Base<Derived2> {
public:
void implementation()
{
std::cout << "Derived2" << std::endl;
}
};
int main()
{
Base<Derived> *pBase = new Base<Derived>();
pBase->method(); // 輸出:"Derived"
delete pBase;
Base<Derived2> *pBase2 = new Base<Derived2>();
pBase2->method(); // 輸出:"Derived2"
delete pBase2;
return 0;
}
雖然使用這種方式實現的多態和面向對象中的多態從功能上說差不多相同,但是前者沒有後者易用、易懂、和能力強大。雖然如此,CRTP作為一種模板設計模式還是很有用的,例如,Boost iterator library就是用了這種方法來實現。
其實在別的語言中也存在CRTP這種模式,如Java,Enum類被定義為Enum<T extends Enum<T>>,當然由於Java在模板方面的不足,作為Java語言的使用者,你是沒法自己體驗這樣的用法(Java雖然支持模板特性,但是用戶不能自己定義模板,只能使用庫裡邊的模板類)。

相關詞條

熱門詞條

聯絡我們