静多态,动多态对比

来源:互联网 发布:数控车床左螺纹编程 编辑:程序博客网 时间:2024/06/16 03:40

静态多态  泛型编程里的常用手法  STL里屡见不鲜。

静多态的静态特性:

所有的类型必须在编译期确定

静多态,动多态对比

通过继承实现的多态是绑定的和动态的:

1,绑定:对于参与多态行为的类型,它们的接口在公共基类中就预先确定

2,动态:接口的绑定是运行期(动态)完成的

通过模式实现的多态是非绑定的和静态的:

1,非绑定:对于参与多态行为的类型,它们的接口没有预先确定

1,  静态:接口的绑定是编译期(静态)完成的

优点

1,相比动多态,静多态有更好的安全性:

因为静多态在编译期对所有的绑定操作进行检查【若把一个错误类型对象插入到容器,编译期可以检查这个错误。】。

2、避免虚指针的内存损耗以及虚函数调用运行效率损耗
缺点:
1
、因为编译时会加入类型的确定,代码尺寸会稍稍大一点
2
、无法对多态对象统一处理,多态性稍有折扣

例子

动多态:虚函数,继承实现

void mydraw(base const&obj)//base是一个抽象基类

{

obj.draw();

}

静多态:模板实现

例子1:演示静态多态

template<class mybase>

void mydraw(mybase const&obj)//mybase是模板参数

{

obj.draw();

}

line l;

circle c;

mydraw(l);//mydraw<line>(mybase&)--->line::draw()

mydraw(c);//mydraw<circle>(mybase&)--->circle::draw()

*****************************************************************

例子2:演示 静态多态+traits技法

此例子原地址:http://blog.csdn.net/pongba/article/details/82783

假设一所大学的注册系统提供了一个注册函数:

template<class T>

void Register(T person)

{

Register(person, typename T::person_tag());

};

而对于注册者有以下几种标识:

struct student_tag{};

struct teacher_tag{};

还有Register的几个供内部使用的重载版本:

template<class T> void Register(T p, student_tag){...}// 注册学生

template<class T> void Register(T p, teacher_tag){...}// 注册教师

并规定学生类一定要在内部typedef student_tagperson_tag,教师类typedef teacher_tag person_tag

这样,当传给起初的那个Register的对象为学生类对象时,typenameT::person_tag()其实就构造了一个student_tag对象,

从而激发函数重载,调用Register内部版本的template<class T> voidRegister(T p,student_tag)版本。其他情况亦均有对应。

这是泛型编程里的常用手法(静态多态),STL里屡见不鲜。

例子3:演示静态多态+动态多态
template<typename T>
class Category2
{
protected:
    static string m_strName;
public:
    virtual void SayCatName()
    {
        T*    pT    = static_cast<T*>(this);
        pT->SayCatName();
    }
};

class Car2 : public Category2<Car2>
{
public:
    virtual void SayCatName()
    {
        cout << m_strName << endl;
    }
};
string Car2::m_strName = "Car2";

class House2 : public Category2<House2>
{
public:
    virtual void SayCatName()
    {
        cout << m_strName << endl;
    }
};
string House2::m_strName = "House2";

// test Template
    Category2<Car2>*    pCar2    = new Car2();
    pCar2->SayCatName();
    delete pCar2;
    pCar2 = NULL;
    Category2<House2>*    pHouse2    = new House2();
    pHouse2->SayCatName();
    delete pHouse2;
    pHouse2 = NULL;

补充:type_traits

template<classT> // T表示接受的是何种动物

voidAcceptAnimals(T animal)

{

...  //dosomething

};

想将猫和狗分开处理(毕竟饲养一只猫和饲养一只狗并不相同。他们可能会为狗买一根链子,而温顺的猫则可能不需要)

方法一:虚函数,但不能用于模板函数

方法二:获取类型T的特征(trait),并按照不同的特征来采用不同的策略

步骤:

1,  每个类的型别定义,即typedef的空类,目的是激发函数重载约定所有的动物类(class Cat,class Dog)都必须在内部typedef一个表明自己身份的类型。作为标识的类型如下:

structcat_tag{}; //这只是个空类,目的是激发函数重载

structdog_tag{}; //同上

2,  类内部的typedef定义

所有狗类都必须像这样:

class Dog

{

public:

// 类型(身份)标志,表示这是狗类,如果是猫类则为typedef cat_tag type;

typedef  dog_tag  type;

..

}

3,  内层函数增加型别定义的类作为参数

//第二个参数为无名参数,只是为了激发函数重载

template<classT>

void Accept(Tdog,dog_tag)

{...}

template<classT>

void Accpet(Tcat,cat_tag) // 同上

{...}

4,外层函数

template<classT>

void Accept(Tanimal)  //这是向外界提供的唯一接口

{

//如果T为狗类,则typename T::type就是dog_tag,那么typenameT::type()就是创建了一个dog_tag类的临时对象,根据函数重载的规则,

这将调用Accept(T,dog_tag),这正是转向处理狗的策略。

//如果T为猫类,则typename T::typecat_tag,由上面的推导,这将调用Accept(T,cat_tag),即转向处理猫的策略,

typename 关键字告诉编译器T::type是个类型而不是静态成员。

Accept(animal,typenameT::type()); // #1

}

注:所有类型推导,函数重载,都在编译期完成

typenameT::type其实就是traits,只不过少了一层封装而已,如果像这样作一些改进:

template<typenameT>

struct AnimalTraits

{

typedef T::typetype;

};

于是,#1处的代码便可以写成:

Accept(animal,typename AnimalTraits<T>::type());

其他traits技法参考博文STL系列《《STL源码剖析》traits技法》

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&


0 0
原创粉丝点击