zoukankan      html  css  js  c++  java
  • 【C++ Primer】类的多态

    一,概述

            1)接口的多种不同的实现方式即为多态。

            2)多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在C++中都是通过虚函数(Virtual Function) 实现的。

            3)关键点:多态还有个关键之处就是一切用指向基类的指针或引用来操作对象


    二,示例

    1)普通的对象操作方式

    #include <iostream>
    using namespace std;
    class A{
    public:
    	void print()
    	{ 
    		cout<<"This is A"<<endl;
    	} 
    };
    class B:public A{
    public:
    	void print()
    	{ 
    		cout<<"This is B"<<endl;
    	}
    };
    int main(){ //为了在以后便于区分,我这段main()代码叫做main1  
    	A a; 
    	B b; 
    	a.print();
    	b.print();
    }

    输出:This  is  A   

               This  is  B

    2)按照多态的操作对象方式:通过父类对象来操作子类对象

    #include <iostream>
    using namespace std;
    class A{
    public:
    	void print()
    	{ 
    		cout<<"This is A"<<endl;
    	} 
    };
    class B:public A{
    public:
    	void print()
    	{ 
    		cout<<"This is B"<<endl;
    	}
    };
    int main(){ //通过父类来操作子类 
    	A a; 
    	B b; 
    	A* p1=&a;
    	A* p2=&b;
    	p1->print();
    	p2->print();
    }

    输出:This  is  A   

               This  is  A

    这跟我们预期的不一样


    3)采用多态,就是在父类函数前面加上virtual

         根据不同的类对象,调用其相应的函数,这个函数就是虚函数

    #include <iostream>
    using namespace std;
    class A{
    public:
    	virtual void print()
    	{ 
    		cout<<"This is A"<<endl;
    	} 
    };
    class B:public A{
    public:
    	void print()
    	{ 
    		cout<<"This is B"<<endl;
    	}
    };
    int main(){ //通过父类来操作子类 
    	A a; 
    	B b; 
    	A* p1=&a;
    	A* p2=&b;
    	p1->print();
    	p2->print();
    }

    三,虚函数进阶

          void (A::*fun)(); //定义一个函数指针   
    A *p=new B;
    fun=&A::fun;//是真正获得虚函数的地址吗? 其实间接获得虚函数地址的一段代码的地址

    #include <iostream>
    using namespace std;
    
    class A{ 
    public:
    	virtual void fun()
    	{ 
    		cout<<"A::fun"<<endl;
    	}
    	virtual void fun2()
    	{
    		cout<<"A::fun2"<<endl;
    	}
    }; 
    class B:public A{
    public:
    	void fun()
    	{ 
    		cout<<"B::fun"<<endl;
    	}
    	void fun2()
    	{
    		 cout<<"B::fun2"<<endl;
     	}
    }; 
    void CallVirtualFun(void *pThis , int  index=0)
    {
    	void (*funptr)(void*);
    	long lVptrAddr;
    	memcpy(&lVptrAddr,pThis,4);
    	memcpy(&funptr,reinterpret_cast<long*>(lVptrAddr)+index,4);
    	funptr(pThis); 
    } 
    int main()
    {
    
    	void (A::*fun)(); //定义一个函数指针   
    	A *p=new B;
    	fun=&A::fun;//是真正获得虚函数的地址吗? 其实间接获得虚函数地址的一段代码的地址
    	(p->*fun)();//B::fun 
    	fun = &A::fun2;
    	(p->*fun)();//B::fun2
    	 
        CallVirtualFun(p); //调用虚函数p->fun() 
    	CallVirtualFun(p,1);//调用虚函数p->fun2() 
    	delete p;
    	system("pause");
    	return 0; 
    } 

    四,补充重要知识

           实例化类的虚函数必须有定义,原因如下:有虚函数作为成员函数的类, 它的实例化-对象, 在运行过程分配到的内存不止是它的成员数据, 还有一个指向该类虚函数表(vtable)的指针, 虚函数表中的每个数据项都是一个虚函数的入口地址; 

           如果一个对象的虚函数只有声明而没有实现, 就会出现这个虚函数表找不到本应作为其数据项之一的某函数的入口地址, 虚函数表在运行前不能装载完成, 所以产生连接错误!








  • 相关阅读:
    从.Net迁移到.Net Core时,需要注意的兼容性变更
    P/Invoke各种总结(五、在C#中使用指针类型)
    P/Invoke各种总结(四、平台调用时的数据类型对应)
    解决Visual Studio 2017/2019 "const char *"类型的值不能用于初始化"char *"类型的实体的问题
    .Net Framework、.Net Core和.Net Standard的区别
    C#代码分析工具Style Cop使用
    《代码不朽:编写可维护软件的10大要则(C#版)》读书笔记
    Visual C++中宽字节与多字节字符互相转换的方法
    浮动和定位
    关于如何设置图片大小和图片形状
  • 原文地址:https://www.cnblogs.com/secbook/p/2654976.html
Copyright © 2011-2022 走看看