Benjamin

静以修身,俭以养德,非澹薄无以明志,非宁静无以致远。
随笔 - 386, 文章 - 0, 评论 - 196, 引用 - 0
数据加载中……

C++之virtual functions(虚函数)实现细节及相关概念

c++中的(static) type和 (dynamic) type 概念是基于多态(polymorphism) ,例如:Vehicle*指针如果实际是指向一个Car对象,那么这个指针的静态类型就是Vechicle,Car则是他的动态类型。静态类型发生在编译器编译时,动态类型发生在动态绑定时。
我们常说的override(覆盖)就是针对虚函数而言。
对虚函数的实现应该说各个编译器是不一样的,大多数的编译器是这样的:
为每一个有虚函数的类增加一个虚表,这个虚表是静态的,还有一个虚指针,为每个类对象。例如:
// Your original C++ source code
 class Base {
 public:
   virtual arbitrary_return_type virt0(...arbitrary params...);
   virtual arbitrary_return_type virt1(...arbitrary params...);
   virtual arbitrary_return_type virt2(...arbitrary params...);
   virtual arbitrary_return_type virt3(...arbitrary params...);
   virtual arbitrary_return_type virt4(...arbitrary params...);
   ...
 };
1 编译器会为这个类的虚函数添加一个虚表,类似下面的:
// Pseudo-code (not C++, not C) for a static table defined within file Base.cpp
 
 // Pretend FunctionPtr is a generic pointer to a generic member function
 // (Remember: this is pseudo-code, not C++ code)
 FunctionPtr Base::__vtable[5] = {
   &Base::virt0, &Base::virt1, &Base::virt2, &Base::virt3, &Base::virt4
 };

2 然后增加一个指向虚表的指针为每一个类对象,这个指针是隐藏的
 // Your original C++ source code
 
 class Base {
 public:
   ...
   FunctionPtr* __vptr;  ← supplied by the compiler, hidden from the programmer
   ...
 };
3 编译器在构造中初始化这个指针
Base::Base(...arbitrary params...)
   : __vptr(&Base::__vtable[0])  ← supplied by the compiler, hidden from the programmer
   ...
 {
   ...
 }
在派生类中,它也会增加一个隐藏的虚表,但是它可以overrides基类的虚函数如:
// Pseudo-code (not C++, not C) for a static table defined within file Der.cpp
 
 // Pretend FunctionPtr is a generic pointer to a generic member function
 // (Remember: this is pseudo-code, not C++ code)
 FunctionPtr Der::__vtable[5] = {
   &Der::virt0, &Der::virt1, &Der::virt2, &Base::virt3, &Base::virt4
 };    

最后看看底层是如何调用的如:                                  
void mycode(Base* p)
 {
   p->virt3();
 }
主要三部分:
1.获取隐藏的指向虚表的指针,并把它放在 register中如r1;
2.获取指针r2=r1+3*4(假定一个指针有四个字节) ,并把它放到register中。
3 根据r2的地址调用函数。

所以说,调用一个虚函数至少和非虚函数差不多.
在这里我们可以看出一个虚指针的长度,至少是四个字节,但是要注意编译器对它的具体实现

纯虚函数怎样用,下面的例子可以说明这个问题。
象下面的代码就可以用纯虚函数来实现:
typedef std::vector<Vehicle*>  VehicleList;
 
 void myCode(VehicleList& v)
 {
   for (VehicleList::iterator p = v.begin(); p != v.end(); ++p) {
     Vehicle& v = **p;  // just for shorthand
 
     // generic code that works for any vehicle...
     ...
 
     // perform the "foo-bar" operation.
     // note: the details of the "foo-bar" operation depend
     // on whether we're working with a car or a truck.
     if (v is a Car) {
       // car-specific code that does "foo-bar" on car v
       ...
     } else if (v is a Truck) {
       // truck-specific code that does "foo-bar" on truck v
       ...
     } else {
       // semi-generic code that does "foo-bar" on something else
       ...
     }
 
     // generic code that works for any vehicle...
     ...
   }
 }
用纯虚函数实现如下:
class Vehicle {
 public:
   // performs the "foo-bar" operation
   virtual void fooBar() = 0;
 };
typedef std::vector<Vehicle*>  VehicleList;
 
 void myCode(VehicleList& v)
 {
   for (VehicleList::iterator p = v.begin(); p != v.end(); ++p) {
     Vehicle& v = **p;  // just for shorthand
 
     // generic code that works for any vehicle...
     ...
 
     // perform the "foo-bar" operation.
     v.fooBar();
 
     // generic code that works for any vehicle...
     ...
   }
 }

也可以用继承的方法来实现
class Car : public Vehicle {
 public:
   virtual void fooBar();
 };
 
 void Car::fooBar()
 {
   // car-specific code that does "foo-bar" on 'this'
   ...  ← this is the code that was in {...} of if (v is a Car)
 }
 
 class Truck : public Vehicle {
 public:
   virtual void fooBar();
 };
 
 void Truck::fooBar()
 {
   // truck-specific code that does "foo-bar" on 'this'
   ...  ← this is the code that was in {...} of if (v is a Truck)
 }
有纯虚函数的是抽象基类,强迫派生类接受基类的接口并实现,在COM(组件)中比较常见,。

virtual constructor(虚构造)的一个实现方法之一:
class Shape {
 public:
   virtual ~Shape() { }                 // A virtual destructor
   virtual void draw() = 0;             // A pure virtual function
   virtual void move() = 0;
   ...
   virtual Shape* clone()  const = 0;   // Uses the copy constructor
   virtual Shape* create() const = 0;   // Uses the default constructor
 };
 
 class Circle : public Shape {
 public:
   Circle* clone()  const;   // Covariant Return Types; see below
   Circle* create() const;   // Covariant Return Types; see below
   ...
 };
 
 Circle* Circle::clone()  const { return new Circle(*this); }
 Circle* Circle::create() const { return new Circle();      }

void userCode(Shape& s)
 {
   Shape* s2 = s.clone();
   Shape* s3 = s.create();
   ...
   delete s2;    // You need a virtual destructor here
   delete s3;
 }
注意在VC6中必须写成Shape*,VC7就不用改,支持返回类型可以变。

posted on 2009-06-13 17:38 Benjamin 阅读(2433) 评论(0)  编辑 收藏 引用 所属分类: C/C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理