词条 | 静多态 |
释义 | 概述静多态(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虽然支持模板特性,但是用户不能自己定义模板,只能使用库里边的模板类)。 |
随便看 |
百科全书收录4421916条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。