虚函数的声明和定义:
		
		★ 虚函数的定义很简单,只要在成员函数原型前加一个关键字  virtual  即可。
			
			★ 如果一个基类的成员函数定义为虚函数,那么,它在所有派生类中也保持为虚函数;即使在派生类中省略了virtual关键字,也仍然是虚函数。
			
			★ 派生类要对虚函数进行中可根据需要重定义,重定义的格式有一定的要求:
			
			       ——与基类的虚函数有相同的参数个数;
			
			       ——与基类的虚函数有相同的参数类型;
			
			       ——与基类的虚函数有相同的返回类型。
			
			★ 若虚函数名称相同,参数不同?
			
			| 
								 虚函数和基类的虚函数不是完全相同,既不构成覆盖,又不构成重载,但是可以编译成功。C++认为派生类中的虚函数是重定义函数,是隐藏的,失去了虚特性。在j基类指针引用或对象调用的过程中始终调用的是基类的虚函数。
								 
							 | 
						
| 
								 对于派生类而言,基类的虚函数被派生类的虚函数隐藏了,如果用派生类的对象直接去调用则只存在派生类的虚函数。对于通过多态(基类指针或引用指向派生类之后)去调用虚函数,则始终是调用的基类的虚函数,此时派生类的虚函数被隐藏了。也就是说:相对基类而言,派生类的虚函数被隐藏,相对派生类而言,基类的虚函数被隐藏。只跟调用的对象有关,跟指向的内容没关。
								 
							 | 
						
| 
								 构造函数为什么不能为虚函数?
								 
								根据虚函数的性质,如果A的构造函数为虚函数,且B类也给出了构造函数,则应该只执行B类的构造函数,不再执行A类的构造函数。这样A就不能构造了。
									 
								 | 
						
为什么需要虚函数:
		
		virtual void base::disp()
			
			{
			
			        cout<<"hello,base"<<endl;
			
			}
			
			通过指针访问disp():
			
			不加virtual时,具体调用哪个版本的disp()只取决于指针本身的类型,和指针所指对象的类型无关。
			
			而加virtual时,具体调用哪个版本的disp()不再取决于指针本身的类型,而是取决于指针所指对象的类型。
			
		| 
							 #include <iostream>
							 
							using namespace std;
							 
							class Base
							 
							{
							 
							        public:
							 
							                virtual int func(int x)
							 
							                {
							 
							                        cout<<"Base::func(int)"<<endl;
							 
							                        return x;
							 
							                }
							 
							};
							 
							class Derived : public Base
							 
							{
							 
							        public:
							 
							                int func(int x)  
							 
							//虚函数要求严格,必须和基类保持一致,即使在前面没有加上virtual,也是虚函数
							 
							                {
							 
							                        cout<<"Derived::func(int)"<<endl;
							 
							                        return x;
							 
							                }
							 
							};
							 
							void test(Base & base)
							 
							{   cout<<"x="<<base.func(5)<<endl;   }
							 
							int main()
							 
							{
							 
							        Base base;
							 
							        Derived derived;
							 
							        test(base);   //执行的是Base::func
							 
							        test(derived);   //执行的是Derived::func
							 
							        cout<<endl;
							 
							        Base & ref = derived;
							 
							        ref.func(7);
							 
							        cout<<endl;
							 
							        Base * pbase = &derived;
							 
							        pbase->func(10);  
							 
							//通过基类指针指向派生类对象之后,再去调用虚析构函数,只跟对象有关,而与指针的类型无关
							 
							        return 0;
							 
							}
							 
							 | 
						
							 //指针访问
							 
							#include <iostream>
							 
							using namespace std;
							 
							class Base
							 
							{
							 
							        public:
							 
							                virtual void display()
							 
							                {   cout<<"Base::display()"<<endl;   }
							 
							        private:
							 
							};
							 
							class Child : public Base
							 
							{
							 
							        public:
							 
							                void display()
							 
							                {   cout<<"Child::display()"<<endl;   }
							 
							};
							 
							int main()
							 
							{//虚函数发生的条件:
							 
							 //1、基类的指针或者引用指向派生类对象
							 
							 //2、派生类覆盖了基类的虚函数
							 
							 //3、通过基类的指针或引用调用虚函数,发生动态多态
							 
							        Base base;
							 
							        Base * pb = &base;
							 
							        pb->display();
							 
							        Child child;
							 
							        pb = &child;
							 
							        pb->display();
							 
							        cout<<"sizeof(Base)"<<sizeof(Base)<<endl;
							 
							        cout<<"sizeof(Child)"<<sizeof(Child)<<endl;
							 
							        cout<<endl;
							 
							        child.display();    //直接去代码区找到display(),然后去执行,没有多态机制
							 
							        cout<<endl;
							 
							        Child * pc = &child;
							 
							        pc->display();    //指针访问,多态
							 
							        cout<<endl;
							 
							        pc=(Child*)&base;    //一般不会这样做
							 
							        pc->display();  
							 
							        pc->Base::display();   //没有多态机制,静态联编
							 
							        return 0;
							 
							}
							 
							 | 
					
总结:形成多态的条件:有继承关系并且派生类中对基类的virtual函数现实了覆盖,利用基类的引用或指针指向派生类对象,再用基类的对象去调用virtual函数的时候会实现多态
		
		虚函数的访问:
		
		对虚函数的访问方式不同,程序具体调用哪个函数可能也会有所不同
		
		1、对象名访问
		
		     ●和普通函数一样,虚函数一样可以通过对象名来调用,此时编译器采用的是静态联编。
			
			     ●通过对象名访问虚函数时, 调用哪个类的函数取决于定义对象名的类型。对象类型是基类时,就调用基类的函数;对象类型是子类时,就调用子类的函数。如:
			
			          obj_base.disp();  //调用基类虚函数
			
			          obj_child.disp(); //调用子类虚函数
			
			     ●在子类中还可以使用作用域运算符来指定调用哪个类的函数。如:
			
			          obj_child.base::disp(); //调用基类虚函数
			
			          obj_child.child::disp();//调用子类虚函数
			
			2、指针访问
		
		      ●使用指针访问非虚函数时,编译器根据指针本身的类型决定要调用哪个函数,而不是根据指针指向的对象类型;
			
			      ●使用指针访问虚函数时,编译器根据指针所指对象的类型决定要调用哪个函数(动态联编),而与指针本身的类型无关。
			
			      ●使用指针访问是虚函数调用的最主要形式。
			
			3、引用访问
		
		      ●使用引用访问虚函数,与使用指针访问虚函数类似
			
			      ●不同的是,引用一经声明后,引用变量本身无论如何改变,其调用的函数就不会再改变,始终指向其开始定义时的函数。因此在使用上有一定限制,但这在一定程度上提高了代码的安全性,特别体现在函数参数传递等场合中,可以将引用理解成一种“受限制的指针”
			
			| 
								 #include <iostream>
								 
								using namespace std;
								 
								class Base
								 
								{
								 
								        public:
								 
								                virtual void display()
								 
								                {   cout<<"Base::display()"<<endl;   }
								 
								                void print()
								 
								                {   cout<<"Base::print()"<<endl;   }
								 
								};
								 
								class Child : public Base
								 
								{
								 
								        public:
								 
								                void display()
								 
								                {   cout<<"Child::display()"<<endl;   }
								 
								                void print()
								 
								                {   cout<<"Child::print()"<<endl;   }
								 
								};
								 
								int main()
								 
								{
								 
								        Base base;
								 
								        Base * pb = & base;
								 
								        pb->print();
								 
								        Base & ref1 = base;
								 
								        ref1.display();
								 
								        cout<<endl;
								 
								        Child child;
								 
								        pb = &child;
								 
								        pb->print();
								 
								        pb->display();
								 
								        cout<<endl;
								 
								        Base & ref2 = child;
								 
								        ref2.print();
								 
								        ref2.display();
								 
								        Base base2;
								 
								        ref2=base2;     //引用一经声明,调用的函数就不会在改变,因为地址是不会改变的,虽然值改变了
								 
								        ref2.display()
								 
								        return 0;
								 
								}
								 
								 | 
							
								 #include <iostream>
								 
								using namespace std;
								 
								class Base
								 
								{
								 
								        public:
								 
								                virtual void display()
								 
								                {   cout<<"Base::display()"<<endl;   }
								 
								                void print()
								 
								                {   cout<<"Base::print()"<<endl;   }
								 
								                void callBase1()        //在成员函数中访问,也会发生虚函数机制
								 
								                {   this->display();   }        //成员函数访问虚函数,要用this指针
								 
								                void callBase2()
								 
								                {   Base::display();   }
								 
								};
								 
								class Child : public Base
								 
								{
								 
								        public:
								 
								                void display()
								 
								                {   cout<<"Child::display()"<<endl;   }
								 
								                void print()
								 
								                {   cout<<"Child::print()"<<endl;   }
								 
								                void callChild1()
								 
								                {   this->display();   }
								 
								                void callChild2()
								 
								                {   Base::display();   }
								 
								};
								 
								int main()
								 
								{
								 
								        Base base;
								 
								        Child child;
								 
								        base.callBase1();
								 
								        child.callChild1();
								 
								        cout<<endl;
								 
								        Base * pb = & base;
								 
								        pb->callBase1();
								 
								        pb->callBase2();
								 
								        cout<<endl;
								 
								        pb=&child;
								 
								        pb->callBase1();
								 
								        pb->callBase2();
								 
								        return 0;
								 
								}
								 
								 | 
						
4、类成员函数中访问
			
			      ●在类内的成员函数中访问该类层次中的虚函数,采用动态联编,要使用this指针。
			
		5、在构造函数或析构函数中访问
		
		      ●构造函数和析构函数是特殊的成员函数,在其中访问虚函数时,C++采用静态联编,即在构造函数或析构函数内,即使是使用“this->虚函数名”的形式来调用,编译器仍将其解释为静态联编的“本类名::虚函数名”。即它们所调用的虚函数是自己类中定义的函数,如果在自己的类中没有实现该函数,则调用的是基类中的虚函数。但绝不会调用任何在派生类中重定义的虚函数。 
			
			虚函数表vftable:
			
			     如果类中包含有虚成员函数,在用该类实例化对象时,对象的第一个成员将是一个指向虚函数表(vftable)的指针(vfptr)。虚函数表记录运行过程中实际应该调用的虚函数的入口地址。
				
				包含虚函数的类的sizeof:
		
		     包含有虚函数的类,在用sizeof求类对象所占用的内存空间的时候,因为此时对象有个指向虚函数表的指针,所以结果应该多四个字节,但是当同一个类中有多个虚函数的时候,也只有4个字节。当另一个类虚基继承时,又会多4个字节。如果派生类中又自己定义了新的虚函数,则又多4个字节。
			
			| 
								 #include<iostream>
									 
									using namespace std;
									 
									class A
									 
									{
									 
									        int i;
									 
									        virtual void fun();
									 
									        virtual void fun1();
									 
									        virtual void fun2();
									 
									};
									 
									// i 4个字节,vfptr指针8个字节,内存对齐 8+4  对齐 16
									 
									class B:virtual public A
									 
									{
									 
									        virtual void fun();
									 
									        virtual void fun1();
									 
									};
									 
									//8 8 4 = 24
									 
									int main()
									 
									{
									 
									        cout<<"sizeof A="<<sizeof(A)<<endl;
									 
									        cout<<"sizeof B="<<sizeof(B)<<endl;
									 
									        return 0;
									 
									}
									 
									//sizeof A=16
									 
									//sizeof B=24
									 
								 | 
							
								 #include<iostream>
								 
								using namespace std;
								 
								class A
								 
								{
								 
								        int i;
								 
								        void f();
								 
								        virtual void fun();
								 
								        virtual void fun1();
								 
								        virtual void fun2();
								 
								};
								 
								class B:virtual public A
								 
								{
								 
								        virtual void fun1();
								 
								        virtual void fun3();
								 
								};
								 
								// 虚继承已经有虚函数表了,fun3()特有的,只要
								 
								// 加入虚函数表就可以了
								 
								int main()
								 
								{
								 
								        cout<<"sizeof A="<<sizeof(A)<<endl;
								 
								        cout<<"sizeof B="<<sizeof(B)<<endl;
								 
								        return 0;
								 
								}
								 
								//sizeof A=16
								 
								//sizeof B=24
								 
							 | 
						
                    
                
                
            
        
浙公网安备 33010602011771号