7.虚函数

2018.6.5 11:13

1.指向对象的指针
father *pfather=new son;//new son 构造了一个子类对象,在构造子类对象之前先构造父类对象,所以会先调用父类构造函数,然后调用子类构造函数pfather指针指向的是父类对象。

son *p=new son.//指向子类对象的指针

delete p;

完整代码

#include <iostream> 
using namespace std; 
class father
{
  public:
  father():age(54){cout<<"调用father类的构造函数并初始化age的值为:"<<age<<"\n";} ~father(){cout<<"调用father类的析构函数\n";}
  void jump()const {cout<<"父亲可以跳五个台阶\n";}
  void run()const{cout<<"父亲可以跑万米\n";}
  protected:
  int age;
};
class son:public father
{
  public:
  son(){cout<<"调用son类的构造函数\n";}
  ~son(){cout<<"调用son类的析构函数\n";}
  void math(){cout<<"儿子会数学\n";}
  void jump()const {cout<<"儿子可以跳十个台阶\n";}
  void run()const{cout<<"儿子可以跑十万米\n";}
};
void main() 
{ 
father *pfather=new son;	//在堆中创建一个派生类的对象并把该派生类的对象赋给基类指针,由于在堆中创建的对象都是匿名的,因此创建的时候必须用new加所属的类名。而且要访问它们必须用指针,new是一个关键字,作用是在堆中创建一个对象,该关键字在创建对象时会自动调用构造函数。
pfather->jump();	   			//用该指针访问父类中的jump函数
pfather->run();	       		//用该指针访问父类中的run函数
//pfather->math();
delete pfather;		  		//删除指针指向的派生类的对象,该语句与new对应使用,使用时将自动调用析构函数删除在堆中创建的对象,并释放内存,这个语句将在后面的数组中有详细的介绍
system("pause");				//暂停程序
}


(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:1
阿里一拳的力量为420磅
(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:2
刘易斯一拳的力量为480磅
(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:3
泰森一拳的力量为500磅
(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:4
霍利菲尔德一拳的力量为350磅
(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:
2.虚函数

解决父指针不能访问子对象的方法

class father
{
    public:
    virtural void run()const{"couut<<father can run 10000\n";}//virtual表示该函数有多种形态。自动判断哪个对象调用了它
    void jump() const{cout<<"father";}
};
class son:public father
{
	public:
    void run()const{"couut<<son can run 10000000\n";}
     void jump() const{cout<<"son";//没有将此函数声明为虚函数,所以该函数不具备多种形态,只具备父类函数的功能。
}
int main()
{
    father *p=new son;//建立父类和子类的联系
    
    p->jump();//调用jump函数时就不会去判断父指针指向的究竟是哪个对象,而直接执行父类jump函数的功能。
    p->run();//由于父类指针指向的是 子类对象,所以调用子类对象的run()函数;
    delete p;
    return 0;
}

当类中有虚函数式,编译器就要记录虚函数的地址,并在构造对象时建立好对象与虚函数的联系,这样在调用虚函数时就可根据父指针指向的对象的类型,调用与该类型相匹配的虚函数。虚函数必须在基类定义才能实现虚函数的作用。而且虚函数只对指针和引用有效,按值传递对象不允许调用虚函数。

拳击游戏:

#include <iostream> 
using namespace std; 
class poser
{
  public:
  virtual void beat()const{cout<<"一般选手一拳的力量为260磅\n";}
  protected:
  int age;
};
class Ali:public poser
{
  public:
  void beat()const{cout<<"阿里一拳的力量为420磅\n";}
};
class Lewis:public poser
{
  public:
  void beat()const{cout<<"刘易斯一拳的力量为480磅\n";}
};
class Tyson:public poser
{
  public:
  void beat()const{cout<<"泰森一拳的力量为500磅\n";} 
};
class holy:public poser
{
  public:
  void beat()const{cout<<"霍利菲尔德一拳的力量为350磅\n";}
};
int main() 
{ 
  poser *a[5];
  poser *p;
  int choice,i;
  for(i=0;i<5;i++)
{
  cout<<"(1)阿里(2)刘易斯(3)泰森(4)霍利菲尔德:";
  cin>>choice;
  switch(choice)
  {
    case 1:p=new Ali; 		//选择1时,新建Ali对象,并用指针p来指向它
    break;   					//跳出switch语句
    case 2:p=new Lewis;
    break;
    case 3:p=new Tyson; 		//选择3时,新建Tyson对象,并用指针p来指向它
    break;
    case 4:p=new holy;		//选择4时,新建holy对象,并用指针p来指向它
    break;
    default:p=new poser; 		//默认时,也就是选其他数字,新建poser对象,并用
       //指针p来指向它
    break;
  }
  a[i]=p; 					//将p指针赋给数组元素,元素中保存的是对象地址,
       //关于数组的概念后面还要详细讲解。
  a[i]->beat();				//通过数组中保存的指针来访问函数,由于是间接访问,所以要
       //加指向成员运算符->
  }  						//for循环结束
  return 0; 
} 

3.继承是否可以实现多态性

**函数联编:**将一个调用函数者联结上正确的被调用函数。

静态联编:未加是virtual说明时,即被调函数和执行调用函数者的关系以及它们的内存地址在编译时都已经确立好,运行时不再发生变化。联编工作出现在编译连接阶段,故又称早期联编

A P;
P=A();
P=B();//x选择子类时仍然执行父类的函数

4.3种调用虚函数的比较

#include <iostream> 
using namespace std;
class father
{
   public:
   virtual void run()const{cout<<"父亲可以跑万米\n";}  //将该函数说明为虚函数
};
class son:public father
{
   public:
   void run()const{cout<<"儿子可以跑十万米\n";}
};
class daughter:public father
{
   public:
   void run()const{cout<<"女儿可以跑十万米\n";}
};
void one(father); 		//该函数原型中声明一个指向father类的对象
void two(father*); 		//该函数原型中声明一个指向father类的指针
void three(father&); 	//该函数原型中声明一个指向father的引用
void main()
{
   father *p=0;
   int choice;
   while(1)
   {
   bool quit=false;
   cout<<"(0)quit (1)son (2)daughter (3)father:";
   cin>>choice;
   switch(choice)
   {
   case 0:quit=true;
   break;
   case 1:p=new son;
   one(*p);				//将对象作为参数传递给函数one()中,由于p是son类对象的内存地址,因此*p代表的是son对象,显然*p是按值传递,所以虚函数不起到多态作用。
   break;
   case 2:p=new daughter;
   two(p);				//将对象的内存地址传递到函数two()中,two接受的参数是指向父类的指针two
   break;
   case 3:p=new father;
   three(*p);			//将对象的引用传递到函数three()中
   break;
   default:cout<<"请输入0到3之间的数字\n";
   break;
}
if (quit)
break;
}
}
void one(father one) 		//函数头中接收一个指向father的对象
{
one.run();  					//用该对象访问run函数
}
void two(father *two) 		//函数头中接收一个指向father的指针
{
two->run(); 					//用该指针访问run()函数
}
void three (father &three)	//函数头中接收一个指向father的引用
{
three.run(); 				//用该引用访问run()函数
}

(0)quit (1)son (2)daughter (3)father:4
请输入0到3之间的数字
(0)quit (1)son (2)daughter (3)father:1
父亲可以跑万米
(0)quit (1)son (2)daughter (3)father:2
女儿可以跑十万米
(0)quit (1)son (2)daughter (3)father:3
父亲可以跑万米
(0)quit (1)son (2)daughter (3)father:0

5.在虚函数中使用成员名限定

可强行接触动态联编

#include "iostream"
using namespace std;
class A
{
  public:
   virtual int get(){return 0;}
};
class B:public A
{
  public:
   int get(){return 1;}
};
void main()
{
  B b;
  A*p=&b;
  cout<<p->get()<<endl;
  cout<<p->A::get(); //成员名限定会强制使用静态联编来调用类A的成员函数
}

1
0

6.虚析构函数

#include "iostream"
using namespace std;
class A
{
  public:
  A(){cout<<"创建A"<<endl;}
  virtual void func(){cout<<"类A"<<endl;}
  virtual ~A(){cout<<"析构A"<<endl<<endl;}
};
class B:public A
{
  public:
  B(){cout<<"创建B"<<endl;}
  void func(){cout<<"类B"<<endl;}
  ~B(){cout<<"析构B"<<endl<<endl;}/不作说明,也是虚析构函数
int main()
{
  A*pa=new A;//new与构造函数一起工作,当使用new来创建一个对象时,也会默认地调用构造函数
  pa->func();
  delete pa; //delete与析构函数一起工作,当使用delete删除一个对象或者指针时,也会默认地调用析构函数。如果该析构函数是虚函数,那么这个调用的过程将是动态的
  A *pb=new B;
  pb->func();
  delete pb;
  system("pause");
  return 0;
}

派生类的析构函数会自动调用基类的析构函数,因此构造的整个对象都会被销毁。所以定义了虚函数,析构函数最好也声明虚函数。

创建A
类A
析构A

创建A
创建B
类B
析构B

析构A

请按任意键继续. . .


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值