C++学习(一)-虚函数(1)

本文深入探讨了C++中的多态性概念,包括编译时和运行时的多态,以及虚函数如何实现动态绑定。通过具体示例,解释了基类与派生类之间函数调用的不同情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,多态性:是指不同对象收到相同的信息,产生不同的动作;简单地说,用一个名字定义不同的函数,这些函数执行不同但又类似的动作。也就是“一个接口,多钟方法”。

2,多态分为:编译时的多态和运行时的多态两种。

3,连编:是把函数名和函数体的程序代码连接在一起的过程。静态连编是在编译时完成的,动态连编是在运行时完成的,也就是在发生调用时,才去寻找和连接程序代码。

4,C++是编译性的,仍采用静态编译,但可以通过虚函数机制实现动态连编。编译时的多态性通过函数重载和运算符重载来实现。

5,(1)当基类指针指向子类对象时,基类指针调用基类和子类相同的函数时,调用的是基类的函数。

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	   void show()
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<" "<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}
	 void show()//和基类相同的函数
	{
      cout<<"调用的是子类函数"<<endl;
	  cout<<"c="<<c<<endl;
	}
private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  
	Base mc (30,20),*pb;
	pb=pc;            //基类指针指向子类对象
	//pc->show();       
	pb->show();     //基类指针调用基类和子类相同的函数时,调用的是基类的函数
	return 0;      
}

     实验结果如下:

  

   (2) 当基类的函数被设置为虚函数时,调用的子类中的函数。

   

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	  virtual void show()//设置为虚函数
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<" "<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}
	 void show()
	{
      cout<<"调用的是子类函数"<<endl;
	  cout<<"c="<<c<<endl;
	}
private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  
	Base mc (30,20),*pb;
	pb=pc;            //基类指针指向子类对象
	//pc->show();       
	pb->show();     //基类指针调用基类和子类相同的函数时,调用的是子类的函数
	return 0;      
}

 实验结果如下:


     (3) 当基类的虚函数带有缺省参数时时,调用的是基类中函数,即缺省参数的虚函数是静态绑定的。

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	  virtual void show(char* value="我是缺省参数")//基类虚函数为缺省参数
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<" "<<value<<"\n"<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}
	 void show()
	{
      cout<<"调用的是子类函数"<<endl;
	  cout<<"c="<<c<<endl;
	}
private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  
	Base mc (30,20),*pb;
	pb=pc;            //基类指针指向子类对象
	//pc->show();       
	pb->show();     //基类指针调用基类和子类相同的函数时,调用的是基类的函数
	return 0;      
}

实验结果如下:


     以上(1),(2),(3),全都是基类对象指针指向子类对象。

     (4),当子类对象指针调用基类和子类相同的函数时,调用的是子类中的函数。

       

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	  void show()//和子类相同的函数
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<"\n"<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}
	 void show()//和基类相同的函数
	{
      cout<<"调用的是子类函数"<<endl;
	  cout<<"c="<<c<<endl;
	}
private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  
	Base mc (30,20),*pb;
	//pb=pc;            //基类指针指向子类对象
	pc->show();         //子类对象指针调用基类和子类相同的函数时,调用的时子类的函数。
	//pb->show();     
	return 0;      
}

实验结果如下:

 

     (5)  子类可以继承基类的函数,并且在一定继承方式下,可以访问它,也就是说,子类没有基类同名的函数,在一定方式下,可以访问基类函数

 

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	  void show()
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<"\n"<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}

private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  //子类对象指针
	Base mc (30,20),*pb;
	//pb=pc;            //基类指针指向子类对象
	pc->show();         //子类对象指针调用基类函数
	//pb->show();     
	return 0;      
}


     实验结果如下:

  

 

     (6),子类对象指针可以指向基类对象,但是必须强制类型转换。此时即使子类对象指针指向基类对象,在调用基类和子类相同函数时,调用的是子类函数。

 

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	  void show()
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<"\n"<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}
	void show()
	{
		cout<<"调用的是子类函数"<<endl;
		//cout<<c<<"\n"<<endl;
	}
private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  //子类对象指针
	Base mc (30,20),*pb;
	pc=(Derived*)&mc;            //子类对象指针指向基类对象
	pc->show();         //子类对象指针调用子类函数
	//pb->show();     
	return 0;      
}


       实验结果:

  

    (7),当子类指针指向基类对象时,必须强制类型转换,若基类和子类中有相同的函数,并且该函数为虚函数,用子类对象指针访问该函数时,访问的是基类中的函数。

  

#include "stdafx.h"
#include "iostream"
using namespace std;

class Base
{
  public:
	  Base(int x,int y)
	  {
		  a=x;
		  b=y;
	  }
	  virtual void show()//设置为虚函数
	  {
		  cout<<"调用的是基类函数"<<endl;
		  cout<<a<<" "<<b<<"\n"<<endl;
	  }
  private:
	  int a;
	  int b;
};

class Derived:public Base
{
public:
	Derived(int x,int y,int z):Base(x,y)
	{
		c=z;
	}
	void show()
	{
		cout<<"调用的是子类函数"<<endl;
		//cout<<c<<"\n"<<endl;
	}
private:
	int c;
};
int main()
{
	Derived *pc=new Derived(30,50,40);  //子类对象指针
	Base mc (30,20),*pb;
	pc=(Derived*)&mc;            //子类对象指针指向基类对象
	pc->show();         //子类对象指针调用基类函数
	//pb->show();     
	return 0;      
}

     实验结果如下:

 

       总结:对于基类和子类中具有的相同函数,对象指针访问函数的选择,依据于其所属的类型,当该函数为虚函数;对象指针访问函数选择,依据于其指向的对象。

     

7,使用对象指针的目的是为了表达一种动态的性质,即当指针指向不同对象时,执行不同的操作。

8,虚函数的定义是在基类中进行的,当某个函数被定义为虚函数,其在一个或多个派生类中可以被重新定义,但是,其函数原型,包括返回值类型,函数名,形参个数,形参类型及顺序,必须与基类中原型完全相同。这也是与重载函数一个不同的地方。

9,通过定义虚函数机制来实现多态时,派生类必须从它的基类中公有继承

10,只有通过基类指针访问虚函数,才能获得运行时的多态性。

11,一个虚函数无论被公有继承多少次,都保有虚函数的特性。


12,虚函数必须是其所在类的成员函数,不能是友元函数,也不能是静态成员函数,因为虚函数调用必须依靠指定的对象来决定该激活哪一个函数。

13,内联函数不能是虚函数,因为内联函数不能再运行时确定其位置。

14,构造函数不能是虚函数,但是析构函数可以是虚函数,并且通常是虚函数。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值