C++模板--ATL利用其实现多态
2016-12-16 18:48
218 查看
即使你能够毫不费力地阅读C++的模板类代码,仍然有两件事可能会使你有些头晕,以下面这个类的定义为例:
CMyWnd:public CWindowImpl<CMyWnd>
{
};
这样作是合法的,因为C++的语法解释说即使CMyWnd类只是被部分定义,类名CMyWnd已经被列入递归继承列表,是可以使用的。将类名作为模板类的参数是因为ATL要做另一件诡秘的事情,那就是编译期间的虚函数调用机制。
如果你想要了解它是如何工作地,请看下面的例子:template <class T>
class B1
{
public:
void SayHi()
{
T* pT = static_cast<T*>(this); // HUH?? 我将在下面解释
pT->PrintClassName();
}
protected:
void PrintClassName() { cout << "This is B1"; }
};
class D1 : public B1<D1>
{
// No overridden functions at all
};
class D2 : public B1<D2>
{
protected:
void PrintClassName() { cout << "This is D2"; }
};
main()
{
D1 d1;
D2 d2;
d1.SayHi(); // prints "This is B1"
d2.SayHi(); // prints "This is D2"
}
这句代码static_cast<T*>(this) 就是窍门所在。它根据函数调用时的特殊处理将指向B1类型的指针this指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。(如果你写成:
class D3 : public B1<D2>就会有麻烦) 之所以安全是因为this对象只可能是指向D1或D2(在某些情况下)类型的对象,不会是其他的东西。注意这很像C++的多态性(polymorphism),只是SayHi()方法不是虚函数。
要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成:
void B1<D1>::SayHi()
{
D1* pT = static_cast<D1*>(this);
pT->PrintClassName();
}由于D1没有重载PrintClassName(),所以查看基类B1,B1有PrintClassName(),所以B1的PrintClassName()被调用。
现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成:
void B1<D2>::SayHi()
{
D2* pT = static_cast<D2*>(this);
pT->PrintClassName();
}
这一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被调用。
这种技术的有利之处在于:
不需要使用指向对象的指针。
节省内存,因为不需要虚函数表。
因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。
所有的函数调用在编译时确定(译者加:区别于C++的虚函数机制使用的动态编连),有利于编译程序对代码的优化。
节省虚函数表在这个例子中看起来无足轻重(每个虚函数只有4个字节),但是设想一下如果有15个基类,每个类含有20个方法,加起来就相当可观了。
CMyWnd:public CWindowImpl<CMyWnd>
{
};
这样作是合法的,因为C++的语法解释说即使CMyWnd类只是被部分定义,类名CMyWnd已经被列入递归继承列表,是可以使用的。将类名作为模板类的参数是因为ATL要做另一件诡秘的事情,那就是编译期间的虚函数调用机制。
如果你想要了解它是如何工作地,请看下面的例子:template <class T>
class B1
{
public:
void SayHi()
{
T* pT = static_cast<T*>(this); // HUH?? 我将在下面解释
pT->PrintClassName();
}
protected:
void PrintClassName() { cout << "This is B1"; }
};
class D1 : public B1<D1>
{
// No overridden functions at all
};
class D2 : public B1<D2>
{
protected:
void PrintClassName() { cout << "This is D2"; }
};
main()
{
D1 d1;
D2 d2;
d1.SayHi(); // prints "This is B1"
d2.SayHi(); // prints "This is D2"
}
这句代码static_cast<T*>(this) 就是窍门所在。它根据函数调用时的特殊处理将指向B1类型的指针this指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。(如果你写成:
class D3 : public B1<D2>就会有麻烦) 之所以安全是因为this对象只可能是指向D1或D2(在某些情况下)类型的对象,不会是其他的东西。注意这很像C++的多态性(polymorphism),只是SayHi()方法不是虚函数。
要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成:
void B1<D1>::SayHi()
{
D1* pT = static_cast<D1*>(this);
pT->PrintClassName();
}由于D1没有重载PrintClassName(),所以查看基类B1,B1有PrintClassName(),所以B1的PrintClassName()被调用。
现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成:
void B1<D2>::SayHi()
{
D2* pT = static_cast<D2*>(this);
pT->PrintClassName();
}
这一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被调用。
这种技术的有利之处在于:
不需要使用指向对象的指针。
节省内存,因为不需要虚函数表。
因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。
所有的函数调用在编译时确定(译者加:区别于C++的虚函数机制使用的动态编连),有利于编译程序对代码的优化。
节省虚函数表在这个例子中看起来无足轻重(每个虚函数只有4个字节),但是设想一下如果有15个基类,每个类含有20个方法,加起来就相当可观了。
相关文章推荐
- [C++] 测试硬件popcnt(位1计数)指令与各种软件算法,利用模板实现静态多态优化性能
- 理解ATL中的ATLNOTABLE和模板实现不带虚函数表之多态
- 利用C++模板,代替虚函数实现类的静态多态性
- C++通过模板实现多态
- C++学习 【4.4】 利用函数实现指定的功能---函数的重载、函数模板、有默认参数的函数
- 利用c++模板实现选择排序
- 利用C++模板,代替虚函数实现类的静态多态性
- cocos2d-x 植物大战僵尸(3)随机太阳因子及利用C++多态实现金币递增
- 利用c++模板实现插入排序
- 利用C++多态实现 “小鸡哔哔”的歌词
- C++利用模板实现一个队列
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- 在C++中利用模板实现 data variant(propery)
- 利用c++模板实现求数组最大值
- 本文是笔者根据数据库编程经验,利用C++语言的模板、继承、授权、多态等面向对象特性,借鉴命令模式,实现了对象在关系数据中的存储,降低应用系统与数据库之间的耦合,提高开发效率。
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- C++中利用虚函数实现多态
- C++中利用模板实现instanceof和is_super_of
- 利用c++模板实现单链表
- 利用c++模板实现冒泡排序