今天问到关于C++的多态性,作为面向对象的三大特性之一,这个也是很基础的,结果一时紧张,只答出来了使用虚函数来实现的多态这一点,后来面试官说还有模板呢啊,恍然大悟,回来后就多态性仔细又看了看,现在总结一下
所谓多态,字面意思就是 多种状态,使用方面统一的接口来实现不同的操作,大家熟知的使用虚函数来实现的多态叫运行时的多态,此外还有静态多态,就是在编译时候就已经确立了状态关系,此外,还流传着两个多态的说法,一说是 函数多态,一说是 宏多态,下面来一一介绍
一、函数多态
我们想,如果要用函数来统一接口,那要怎么办,自然而然想到的是函数重载,使用相同的函数名,但是函数参数列表不论是类型或者长度却不相同,用这种方法来实现多态,不过我想函数重载和模板就多态来讲其实是一样的,只不过是重载函数的话我们要写多个函数,而模板的话我们使用typedef,只要写一个函数,在调用的时候,会根据参数的类型自己去替换匹配而已。不过如果对于不同类型的操作,函数的实现方法不一样而已
- #include<iostream>
- #include<string>
- using namespace std;
- int add(int num1,int num2)
- {
- return num1+num2;
- }
- int add(int num1,string str1)
- {
- return num1+atoi(str1.c_str());
- }
- void main()
- {
- /*函数多态*/
- int num1=10;
- int num2=20;
- int result=add(num1,num2);
- cout<<"函数多态 add(int num1,int num2)的结果是:"<<result<<endl;
- string str1="22";
- result=add(num1,str1);
- cout<<"函数多态 add(int num1,string str1)的结果是:"<<result<<endl;
- }
这样的话编译器会根据不同的参数列表,对函数进行重新整合,两个重载函数的函数名字就会发生改变
二、宏多态
宏多态,就是在定义的宏中含有变量,这样在宏替换的时候,相应的变量也替换了,这样宏中可以带任意的参数类型
- #include<iostream>
- #include<string>
- using namespace std;
- #define ADD(A,B) A+B
- void main()
- {
- /*宏多态*/
- int num1=10;
- int num2=20;
- string str1="22";
- string str2="33";
- cout<<"宏多态 ADD(A+B) A+B:"<<ADD(num1,num2)<<endl;
- cout<<"宏多态 ADD(A+B) A+B:"<<ADD(str1,str2)<<endl;
- }
三、动态多态
在C++中主要使用继承机制和虚函数来实现,通过基类的指针或者引用,指向子类对象,来调用虚函数,这样会导致相应对象的虚函数被调用,因为我们统一使用的基类指针或引用,所以在编译时候不能确定调用的是那个虚函数,或许虚函数最方便的地方时在处理一个集合中,各个对象不同的时候,如下代码
- #include<iostream>
- #include<string>
- #include<vector>
- using namespace std;
- /*虚函数机制*/
- class Base
- {
- public:
- virtual void print()=0;
- };
- class Child1:public Base
- {
- void print()
- {
- cout<<"我是Child1类"<<endl;
- }
- };
- class Child2:public Base
- {
- void print()
- {
- cout<<"我是Child2类"<<endl;
- }
- };
- /*如果容器中函数的对象是不同类的,那么这时候用使用多态是非常的方便的*/
- void function(const vector<Base*>&V)
- {
- for(int i=0;i<V.size();++i)
- {
- V.at(i)->print();
- }
- }
- void main()
- {
- /*虚函数多态*/
- Child1 child1;
- Child2 child2;
- Base *p;
- p=&child1;
- p->print();
- p=&child2;
- p->print();
- vector<Base*>BaseVector;
- BaseVector.push_back(&child1);
- BaseVector.push_back(&child2);
- function(BaseVector);
- }
四、静态多态
如果使用模板来实现上面那个例子,那么我们不需要让这两个类继承同一个父类,在编译的时候,会产生两个不同的函数,静态多态会导致程序变大,但是这样的话会比运行时的多态效率更高,但是我们使用函数模板的话就没办法处理异质对象了,就是在函数模板中如果操作容器的话,而容器里面的对象又不相同,因为我们没办法把两个不同类型的对象放到同一个容器中,所以更常用的方法是将动态多态和静态多态结合起来
- #include<iostream>
- #include<string>
- #include<vector>
- using namespace std;
- /*虚函数机制*/
- class Base
- {
- public:
- virtual void print()=0;
- };
- class Child1:public Base
- {
- void print()
- {
- cout<<"我是Child1类"<<endl;
- }
- };
- class Child2:public Base
- {
- void print()
- {
- cout<<"我是Child2类"<<endl;
- }
- };
- /*如果容器中函数的对象是不同类的,那么这时候用使用多态是非常的方便的*/
- void function(const vector<Base*>&V)
- {
- for(int i=0;i<V.size();++i)
- {
- V.at(i)->print();
- }
- }
- /*静态多态,使用模板*/
- template<typename T>
- void TempalteFuncion(const vector<T*>&V)
- {
- for(int i=0;i<V.size();++i)
- {
- V.at(i)->print();
- }
- }
- void main()
- {
- /*虚函数多态 模板多态混用*/
- Child1 child1;
- Child2 child2;
- Base *p;
- p=&child1;
- p->print();
- p=&child2;
- p->print();
- vector<Base*>BaseVector;
- BaseVector.push_back(&child1);
- BaseVector.push_back(&child2);
- function(BaseVector);
- TempalteFuncion(BaseVector);
- }
总结(摘)
动态多态只需要一个多态函数,生成的可执行代码尺寸较小,静态多态必须针对不同的类型产生不同的模板实体,尺寸会大一些,但生成的代码会更快,因为无需通过指针进行间接操作。静态多态比动态多态更加类型安全,因为全部绑定都被检查于编译期。正如前面例子所示,你不可将一个错误的类型的对象插入到从一个模板实例化而来的容器之中。此外,正如你已经看到的那样,动态多态可以优雅地处理异质对象集合,而静态多态可以用来实现安全、高效的同质对象集合操作。
静态多态为C++带来了泛型编程(generic programming)的概念。泛型编程可以认为是“组件功能基于框架整体而设计”的模板编程。STL就是泛型编程的一个典范。STL是一个框架,它提供了大量的算法、容器和迭代器,全部以模板技术实现。从理论上讲,STL的功能当然可以使用动态多态来实现,不过这样一来其性能必将大打折扣。
静态多态还为C++社群带来了泛型模式(generic patterns)的概念。理论上,每一个需要通过虚函数和类继承而支持的设计模式都可以利用基于模板的静态多态技术(甚至可以结合使用动态多态和静态多态两种技术)而实现。正如你看到的那样,Andrei Alexandrescu的天才作品Modern C++ Design: Generic Programming and Design Patterns Applied(Addison-Wesley)和Loki程序库已经走在了我们的前面。