2008年8月7日星期四

C++中的多态和虚函数

RT

今天的学习笔记~全部内容请看代码~

修改日期:2008-8-7
--------------------------------------------------
// 静态联编

// 在编译时就能够确定哪个重载的成员函数被调用的情况被称为静态联编(Static Binding)
// 当子类和父类某个成员函数相同时(同返回类型,同参数,同名)子类的成员函数
// 可以安全的覆盖父类的成员函数

class Animal
{
 public:
  Animal();
  ~Animal();
  int ReadHead();
  void SetHead(int n); 
  void Bark(); // 用于静态联编的例子

 protected:
  int m_nHead;
};

class Bear : public Animal
{
 public:
  Bear();
  ~Bear();
  int GetLegs();
  void Bark(); // 子类的成员函数

 private:
  int m_nLegs;
};


#include <conio.h>
#include <iostream>
#include "SolveHead.h"
using namespace std;

int main()
{
 Animal MyAni;
 Bear MyBear;

 MyBear.Bark(); // 调用子类的Bark成员函数
 MyAni.Bark();  // 调用父类的Bark成员函数

 // 输出结果为
 // Bear is Barking now
 // Animal is Barking now

 cout<<endl<<"Press any key to exit!"<<endl;
 getch();
  return 0;
}

// --------------------------
// Bear Class - child class

void Bear::Bark()
{
 cout<<"Bear is Barking now"<<endl;
}

Bear::Bear()
{
 Bear::m_nHead = 1;
 Bear::m_nLegs = 4;
}

Bear::~Bear()
{
 
}

int Bear::GetLegs()
{
 return m_nLegs;
}

// -----------------------------
// Animal Class - Derived class

void Animal::Bark()
{
 cout<<"Animal is Barking now"<<endl;
}

Animal::Animal()
{
 
}

Animal::~Animal()
{

}

int Animal::ReadHead()
{
 return m_nHead;
}

void Animal::SetHead(int n)
{
 m_nHead = n;
}

--------------------------------------------------
// 静态联编带来的麻烦

// 有时候,静态联编会导致编译器对于被联编函数的所属搞不清楚
// 看代码.我们仅仅增加一个和类无关的函数

void Biding(Animal &temp)  // 参数类型是父类
{
 temp.Bark();
}

int main()
{
 Animal MyAni;
 Bear MyBear;

 Biding(MyBear);
 Biding(MyAni);

 // 输出的结果是           // 这里编译器无法区分传递的是父类还是子类
 // Animal is Barking now      // 调用的都是父类的Bark成员函数
 // Animal is Barking now      // 解决的方法请看下面的--动态链边和虚函数

 cout<<endl<<"Press any key to exit!"<<endl;
 getch();
  return 0;
}

--------------------------------------------------
// 动态联编和虚函数

// 上面的例子中我们发现了静态联编带来的麻烦
// 为了解决这个问题,C++提供了另外一种多态技术--动态联编
// 动态联编可以使用虚函数,在运行时对调用对象进行确定

class Animal
{
 public:
  Animal();
  ~Animal();
  int ReadHead();
  void SetHead(int n); 
  virtual void Bark(); // 注意这里,声明为虚函数.父类的Bark虚函数

 protected:
  int m_nHead;
};

class Bear : public Animal
{
 public:
  Bear();
  ~Bear();
  int GetLegs();
  virtual void Bark(); // 子类的Bark虚函数

 private:
  int m_nLegs;
};


#include <conio.h>
#include <iostream>
#include "SolveHead.h"
using namespace std;

void Biding(Animal &temp)
{
 temp.Bark();
}

int main()
{
 Animal MyAni;
 Bear MyBear;

 // 注意这里使用虚函数的效果
 Biding(MyBear);
 Biding(MyAni);

 // 输出结果分别是
 // Bear is Barking now
  // Animal is Barking now

 cout<<endl<<"Press any key to exit!"<<endl;
 getch();
  return 0;
}

// --------------------------
// Bear Class - child class

void Bear::Bark()  // 子类虚函数.不能再加virtual
{
 cout<<"Bear is Barking now"<<endl;
}

Bear::Bear()
{
 Bear::m_nHead = 1;
 Bear::m_nLegs = 4;
}

Bear::~Bear()
{
 
}

int Bear::GetLegs()
{
 return m_nLegs;
}

// -----------------------------
// Animal Class - Derived class

void Animal::Bark()  // 父类虚函数.这里不能再加virtual
{
 cout<<"Animal is Barking now"<<endl;
}

Animal::Animal()
{
 
}

Animal::~Animal()
{

}

int Animal::ReadHead()
{
 return m_nHead;
}

void Animal::SetHead(int n)
{
 m_nHead = n;
}

--------------------------------------------------
// 使用虚函数要遵守的规则 [FWD]

// 1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同
// 那么即使加上了virtual关键字,也是不会进行滞后联编的。 

// 2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象
// 所以普通函数不能说明为虚函数。 

// 3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。 

// 4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。
// 即使虚函数在类的内部定义定义,但是在编译的时候系统仍然将它看做是非内联的。 

// 5.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间
// 只有构造完成后,对象才是具体类的实例。 

// 6.析构函数可以是虚函数,而且通常声名为虚函数。 

--------------------------------------------------
// virtual的析构函数

// 如果不使用虚析构函数,那么会导致类似静态联编的问题
// 即在某些动态分配的情况下,子类的析构函数无法调用
// 我们在原来的类代码不变的情况在,在CPP文件中测试非虚析构函数带来的问题

void DelBiding(Animal *temp)
{
 delete temp; // 删除分配的空间
}

int main()
{
 Bear* pBear = new Bear();  // 声明一个子类对象,并动态分配内存空间

 cout<<"Begin delete pointer"<<endl;
 DelBiding(pBear);  // 注意这里
           // 只输出了Destruting Animal Class now!
           // 只调用父类的析构函数,不会调用子类的析构函数
 cout<<endl<<"Press any key to exit!"<<endl;
 getch();
  return 0;
}

// --------------------------
// Bear Class - child class

Bear::Bear()
{
 Bear::m_nHead = 1;
 Bear::m_nLegs = 4;
}

Bear::~Bear()
{
 cout<<"Destructing Bear Class now!"<<endl;
}

int Bear::GetLegs()
{
 return m_nLegs;
}

// -----------------------------
// Animal Class - Derived class

Animal::Animal()
{
 
}

Animal::~Animal()
{
 cout<<"Destructing Animal Class now!"<<endl;
}

int Animal::ReadHead()
{
 return m_nHead;
}

void Animal::SetHead(int n)
{
 m_nHead = n;
}


// 然后我们把子类的父类的析构函数都声明为虚函数
// 在用同样的代码进行测试

class Animal
{
 public:
  Animal();
  virtual ~Animal(); // 虚父类析构函数
  int ReadHead();
  void SetHead(int n); 

 protected:
  int m_nHead;
};

class Bear : public Animal
{
 public:
  Bear();
  virtual ~Bear(); // 虚子类析构函数
  int GetLegs();

 private:
  int m_nLegs;
};


int main()
{
 Bear* pBear = new Bear();  // 动态分配内存空间

 cout<<"Begin delete pointer"<<endl;
 DelBiding(pBear);  // 动态删除
 // 输出结果
 // Begin delete pointer
 // Destructing Bear Class now!
 // Destructing Animal Class now!

 cout<<endl<<"Press any key to exit!"<<endl;
 getch();
  return 0;
}

没有评论:

发表评论

1、可以使用<b>、<i>、<a>等Html标志,让评论更有特色...
2、支持OpenID登录,技术达到国际先进水平。但切记,评论内容不代表本站观点!
3、当遇到“连接被重置”、“连接超时”和“此网页无法访问”等而发表不了评论的话,请多刷新几次页面,或迟三分钟后再试;
4、对你的浏览造成不便,站长在此代表全国G.FW工作人员向你鞠躬致歉!!!