Wednesday 6 February 2019

C++ 多态和虚函数

在类的定义中,前面有virtual关键字的成员函数就是虚函数。
class base{
      virtual int get();
};
int base::get(){}
virtual关键字只用在类定义里的函数声明中,写函数体时不用。
多态一:
派生类的指针可以赋给基类指针,通过基类指针调用基类和派生类中的同名虚函数时:
  1. 若该指针指向一个基类的对象,那么被调用是基类的虚函数;
  2. 若该指针指向一个派生类的对象,那么被调用是派生类的虚函数
class CBase {
public:
 virtual void SomeVirtualFunction(){}
};
class CDerived :public CBase {
 public:
 virtual void SomeVirtualFunction(){}
};
int main() {
 CDerived ODerived;
 CBase *p = &ODerived;
 p->SomeVirtualFunction();
 return 0;
}
多态二:
派生类的对象可以赋给基类引用,通过基类指针调用基类和派生类中的同名虚函数时:
  1. 若该引用引用的是一个基类的对象,那么被调用是基类的虚函数;
  2. 若该引用引用的是一个派生类的对象,那么被调用是派生类的虚函数
class CBase {
public:
 virtual void SomeVirtualFunction(){}
};
class CDerived :public CBase {
 public:
 virtual void SomeVirtualFunction(){}
};
int main() {
 CDerived ODerived;
 CBase &r = ODerived;
 r.SomeVirtualFunction();
 return 0;
}
多态例一:
#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;
class CShape {
public:
 virtual double Area() = 0; //纯虚函数
 virtual void PrintInfo() = 0;
};
class CRectangle :public CShape {
public:
 int w, h;
 virtual double Area();
 virtual void PrintInfo();
};
double CRectangle::Area() {
 return w*h;
}
void CRectangle::PrintInfo() {
 cout << "Rectangle:" << Area() << endl;
}
class CCircle :public CShape {
public:
 int r;
 virtual double Area();
 virtual void PrintInfo();
};
double CCircle::Area() {
 return 3.14*r*r;
}
void CCircle::PrintInfo() {
 cout << "Circle:" << Area() << endl;
}
class CTriangle :public CShape {
public:
 int a,b,c;
 virtual double Area();
 virtual void PrintInfo();
};
double CTriangle::Area() {
 double p = (a + b + c) / 2.0;
 return sqrt(p * (p - a)*(p - b)*(p - c));
}
void CTriangle::PrintInfo() {
 cout << "Triangle:" << Area() << endl;
}
CShape *pShapes[100];
int MyCompare(const void *s1, const void *s2);
int main() {
 int i; int n;
 CRectangle *pr; CCircle *pc; CTriangle *pt;
 cin >> n;
 for (i = 0; i < n; i++) {
  char c;
  cin >> c;
  switch (c) {
  case'R':
   pr = new CRectangle();
   cin >> pr->w >> pr->h;
   pShapes[i] = pr;
   break;
  case'C':
   pc = new CCircle();
   cin >> pc->r;
   pShapes[i] = pc;
   break;
  case 'T':
   pt = new CTriangle();
   cin >> pt->a >> pt->b >> pt->c;
   pShapes[i] = pt;
   break;
  }
 }
 qsort(pShapes, n, sizeof(CShape*), MyCompare);
 for (i = 0; i < n; i++)
  pShapes[i]->PrintInfo();
 return 0;
}
int MyCompare(const void * s1, const void * s2)
{
 double a1, a2;
 CShape * * p1; // s1,s2 是 void * ,不可写 “* s1”来取得s1指向的内容
 CShape * * p2;
 p1 = (CShape * *)s1; //s1,s2指向pShapes数组中的元素,数组元素的类型是CShape *
 p2 = (CShape * *)s2; // 故 p1,p2都是指向指针的指针,类型为 CShape **
 a1 = (*p1)->Area(); // * p1 的类型是 Cshape * ,是基类指针,故此句为多态
 a2 = (*p2)->Area();
 if (a1 < a2)
  return -1;
 else if (a2 < a1)
  return 1;
 else
  return 0;
}
 多态实现的原理:
每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着虚函数表的指针。虚函数表中列出了该类的虚函数地址。多出来的4个字节就是用来放虚函数表的地址的。
纯虚函数:没有函数体的虚函数
class A{
private:
  int a;
public:
  virtual void Print()=0;
  void fun(){cout<<"fun";}
};
抽象类:包括纯虚函数的类
  1. 只能作为基类来派生新类使用
  2. 不能创建抽象类的对象
  3. 抽象类的指针和引用
class A {
 public:
 virtual void f() = 0; //纯虚函数
 void g( ) { this->f( ); } //ok
 A( ){ }   //f( ); // 错误
};
class B : public A{
 public:
 void f(){ cout<<"B: f()"<<endl; }
};
int main(){
B b;
b.g();
return 0;
}
抽象类中,
  1. 在成员函数内可以调用纯虚函数
  2. 在构造函数/析构函数内部不能调用纯虚函数
如果一个类从抽象类派生而来,它实现了基类中的所以纯虚函数,才能成为非抽象类。

No comments:

Post a Comment