很多人在发帖或文章时根本就没有做过必要的验证;事实上从外文翻译过来的经典教材也不是完全没有失误,当然我更相信是翻译造成的问题,但对于理解的人来说错误的例子会造成极恶劣的恶果,就是距离真正的概念失之毫厘,谬以千里 |
C++一个多继承的例子中,由类A,类B1和类B2以及类C组成了类继承的层次结构。在该结构中,类C的对象将包含两个类A的子对象。由于类A是派生类C两条继承路径上的一个公共基类,那么这个公共基类将在派生类的对象中产生多个基类子对象。如下图当构造一个Class C 实例时会构造出2个Class A实例;(java因为不允许从多个类派生;所以避免了这个问题;Class C extend B1,B2是错误的)
/ /
B1{b} B2{c}
/ /
C{f2(),d}
如果要想使这个公共基类在派生类中只产生一个基类子对象,则必须将这个基类设定为虚基类。
虚基类的引入和说明
解释一下什么是纯虚类(抽象):至少有一个成员函数为<纯虚函数>,即没有任何实现但定义明确的虚函数很象java里的接口(Interface);注意一定是纯虚函数
//file classtest.h complied pass by vc60 #ifndef CLASSTEST_H #define CLASSTEST_H #include <stdio.h>
#include <iostream.h> #include <string.h> class A {
public: virtual int f1(int sv1)=0; virtual int f2(int sv2)=0; virtual int f3(int sv3)=0; };
class B:virtual public A
{ public: virtual int f1(int sv1){cout<<"B::f1()"<<endl;return sv1;}; virtual int f3(int sv3){cout<<"B::f3()"<<endl;return sv3+3;}; public: B(){cout<<"B::Constructed"<<endl;}; virtual ~B(){cout<<"B::Destoryed"<<endl;}; }; class C :virtual public A
{ public: virtual int f2(int sv2){cout<<"C::f1()"<<endl;return sv2+2;}; virtual int f3(int sv3){cout<<"C::f3()"<<endl;return sv3+6;}; public: C(){cout<<"C::Constructed"<<endl;}; virtual ~C(){cout<<"C::Destoryed"<<endl;}; }; class D :public B,public C
{ public: D(){} virtual ~D(){} int f1(int sv1){int in=sv1*5; return in;}; int f2(int sv2){return f1(sv2);}; int f3(int sv3){return sv3*9;}; }; #endif |
由于使用了虚基类,使得类A,类B,类C和类D之间关系用DAG图示法表示如下:
/ /
B{b} C{c}
/ /
D{f2(),d}
n.f1(); //对f()引用是正确的。
int D::f2()
{
return f1(sv2); //对f1()引用是正确的。
}
//file classtest.cpp #include "classtest.h" int main(int argc, char* argv[]) { //下面程序段是正确的。 D n; A *pa; pa = &n; cout<<"D obj test:"<<n.f1(100)<<endl; cout<<"A obj test:"<<pa->f1(100)<<endl; return 0; } |
-------------------------------------
Output:
B::Constructed
C::Constructed
D obj test:500
A obj test:500
C::Destoryed
B::Destoryed
其中,pa是指向类A对象的指针,n是类D的一个对象,&n是n对象的地址。pa=&n是让pa指针指向类D的对象,这是正确的,并且也无二义性。
Pa是基类的指针但最后指向一个真正的对象却是子类的对象的实例;与JAVA中的动态绑定作用 dynamic binding/runtime binding是相同的;但C++继承了C语言的规范;更多的是使用所谓"前绑定的方式-early banding";简单的说就是编译器明确知道实例指向的对象类型;类型是严格定义的;JAVA中除了static和 final方法<private方法隐含有final的意思>以外通常都是后绑定
相对于C++的虚基类;JAVA的概念叫做"抽象类"abstract class 概念不再解释;多态性与动态绑定举个例子
import java.util.*;
abstract class A {
int i; // storage allocated for each public abstract void play(); public String what() { return "A"; } public abstract void adjust(); } class B1 extends A {
public void play() { System.out.println("B1.play()"); } public String what() { return "B1"; } public void adjust() {} } class B2 extends A {
public void play() { System.out.println("B2.play()"); } public String what() { return "B2"; } public void adjust() {} } class C extends B1 {
public void play() { System.out.println("C.play()"); } public void adjust() { System.out.println("C.adjust()"); } } public class D {
// Doesn't care about type, so new types // added to the system still work right: static void tune(A i) { i.play(); } static void tuneAll(A[] e) { for(int i = 0; i < e.length; i++) tune(e[i]); } public static void main(String[] args) { A[] orchestra = new A[4]; int i = 0; orchestra[i++] = new B1(); orchestra[i++] = new B2(); orchestra[i++] = new C(); tuneAll(orchestra); } } |
class C extends B1 {
public void play() { System.out.println("C.play()");//overriden } public void play(String sv) { System.out.println("C.play()"+sv); //overload } public void adjust() { System.out.println("C.adjust()"); } } |
#include <iostream.h> class B : virtual public A class C : virtual public A
{ public: C(const char *s1, const char *s2):A(s1) { cout<<s2<<endl; } }; class D : public B, public C
{ public: D(const char *s1, const char *s2, const char *s3, const char *s4) :B(s1, s2), C(s1, s3), A(s1) { cout<<s4<<endl; } }; void main()
{ D *ptr = new D("class A", "class B", "class C", "class D"); delete ptr; } |
class B
class C
class D