📜  C++ 和Java中的默认虚拟行为有何不同?(1)

📅  最后修改于: 2023-12-03 15:13:59.768000             🧑  作者: Mango

C++ 和 Java中的默认虚拟行为有何不同?

C++ 和 Java 都支持虚拟函数,但是它们的默认虚拟行为有一些不同。本文将从以下几个方面进行介绍:

  1. 虚函数声明
  2. 对象大小
  3. 虚函数表(VTable)
  4. 多继承
虚函数声明

在 C++ 中,如果一个类的方法被声明为 virtual,那么它就是一个虚函数。而在 Java 中,如果一个类的方法被声明为 abstract,那么它就是一个虚函数。

C++ 示例代码:

class Animal {
public:
    virtual void speak();
};

class Cat : public Animal {
public:
    void speak() override;
};

Java 示例代码:

abstract class Animal {
    public abstract void speak();
}

class Cat extends Animal {
    public void speak() {
        // ...
    }
}
对象大小

在 C++ 中,每个对象都包含一个指向虚函数表(VTable)的指针,因此一个对象的大小比其成员变量的大小大一个指针的大小。而在 Java 中,虚函数表是由虚拟机来维护的,不会影响对象的大小。

C++ 示例代码:

class Animal {
public:
    virtual void speak();
private:
    int age;
    char *name;
};

class Cat : public Animal {
public:
    void speak() override;
private:
    char *color;
};

int main() {
    cout << sizeof(Animal) << endl; // 8 or 16 (depending on machine)
    cout << sizeof(Cat) << endl; // 16 or 32 (depending on machine)
    return 0;
}

Java 示例代码:

abstract class Animal {
    public abstract void speak();
}

class Cat extends Animal {
    private int age;
    private String name;
    private String color;
    
    public void speak() {
        // ...
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(ObjectSizeCalculator.getObjectSize(new Cat()));
    }
}

// Output: 40 or 48 (depending on JVM and platform)
虚函数表(VTable)

在 C++ 中,每个有虚函数的类都有一个虚函数表。虚函数表是一个包含指向虚函数的指针的数组,每个对象都有一个指向其所属类的虚函数表的指针。当调用一个虚函数时,实际上是通过对象指针中的虚函数表指针找到虚函数表,并在其中找到指向实际函数的指针,然后进行调用。

C++ 示例代码:

class Animal {
public:
    virtual void speak();
};

class Cat : public Animal {
public:
    void speak() override;
};

int main() {
    Animal *a = new Cat();
    a->speak(); // Calls Cat's speak() method
    return 0;
}

在上面的示例代码中,a 实际上是一个 Cat 对象的指针,但是它的静态类型为 Animal。当执行 a->speak() 时,调用的实际上是 Cat 类中的 speak() 方法,但是这是通过虚函数表来实现的。

而在 Java 中,继承树的静态结构已经在编译时确定了,虚表也在编译时被创建。当调用一个虚函数时,虚拟机直接找到虚函数表中指向实际函数的指针,然后进行调用。

Java 示例代码:

abstract class Animal {
    public abstract void speak();
}

class Cat extends Animal {
    public void speak() {
        // ...
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Cat();
        a.speak(); // Calls Cat's speak() method
    }
}
多继承

在 C++ 中,如果一个类继承自多个类,那么它就需要解决菱形继承的问题。C++ 使用虚拟继承(Virtual Inheritance)的方式来解决这个问题。在虚拟继承的情况下,虚函数表指针不是在对象中存储的,而是通过一个指向共享虚函数表的指针来共享。

C++ 示例代码:

class Animal {
public:
    virtual void speak();
};

class Mammal : virtual public Animal {
public:
    void lactate();
};

class Bird : virtual public Animal {
public:
    void fly();
};

class Platypus : public Mammal, public Bird {
public:
    void layEggs();
};

int main() {
    Platypus p;
    p.speak(); // Calls Animal's speak() method
    return 0;
}

而在 Java 中,由于不允许多重继承,因此不存在菱形继承的问题,也就不需要虚拟继承的方式来解决。

Java 示例代码:

abstract class Animal {
    public abstract void speak();
}

class Bird extends Animal {
    public void fly() {
        // ...
    }
}

class Mammal extends Animal {
    public void lactate() {
        // ...
    }
}

class Platypus extends Mammal {
    public void layEggs() {
        // ...
    }
}

public class Main {
    public static void main(String[] args) {
        Platypus p = new Platypus();
        p.speak(); // Calls Mammal's speak() method
    }
}
结论

C++ 和 Java 都支持虚函数,但是它们的默认虚拟行为有一些不同。C++ 中每个对象都包含一个指向虚函数表的指针,而 Java 中虚函数表是由虚拟机来维护的。另外,在多重继承的情况下,C++ 使用虚拟继承来解决菱形继承的问题,而 Java 不允许多重继承,因此不存在菱形继承的问题。