c++swiftinheritancevtable

Inheritance and Visibility in Swift


I'm confused about the content in the swift doc:

Notice that the initializer for the EquilateralTriangle class has three different steps:

  1. Setting the value of properties that the subclass declares.
  2. Calling the superclass’s initializer.
  3. Changing the value of properties defined by the superclass. Any additional setup work that uses methods, getters, or setters can also be done at this point.

And then I try the example below in Swift and C++:

Swift
class Base {
    init() {
        print("Enter Base");
        setUp();
        print("Leave Base");
    }
    func setUp() -> Void {
        print("base setUp()")
    }
}

class Derived: Base {  
    let number: Int;
    override init () {      
        number = 5;
    }
    override func setUp() -> Void {
        print("derived setUp() \(number)")
    }
}

let d = Derived()
let b = Base()
Enter Base
derived setUp() 5
Leave Base
Enter Base
base setUp()
Leave Base
C++
#include <iostream>
#include <string>

class Base {
public:
    Base() {
        std::cout << "Enter Base" << std::endl;
        setUp();
        std::cout << "Leave Base" << std::endl;
    }
    virtual void setUp() {
        std::cout << "base setUp()" << std::endl;
    }
};

class Derived: public Base {  
    int number;
public:
    Derived () {
        number = 5;
    }
    void setUp() override {
        std::cout << "derived setUp() " << number << std::endl;
    }
};

int main()
{
  Derived d = Derived();
  Base b = Base();
}
Enter Base
base setUp()
Leave Base
Enter Base
base setUp()
Leave Base

Based on the two outputs above, it is evident that there are differences between C++ and Swift in terms of their inheritance philosophy. Could someone explain why the behavior differs between the two languages (Why does Swift allow a superclass to access subclass methods)?

I've already know the vtable in C++ and its mechanism, but I know nothing about Swift, so it would be nice if you could compare the difference of the underlying implementation between two languages.


Solution

  • In C++, the base class is fully initialised before the derived class is. Calling the superclass constructor is one of the first things that the derived class constructor does. This means that if you were to call the derived class's setUp in the base class constructor, setUp would observe an uninitialised number. This is undesirable, because whoever wrote setUp might be assuming that all the fields are initialised (though this is less likely to happen when the method is called setUp). Therefore, virtual method calls in a constructor are designed to be dispatched statically. See also this post for more details.

    On the other hand, Swift initialises the classes in the opposite order. Derived classes are initialised first. super.init can only be called after you have initialised all the stored properties that are declared in the derived class.

    class Derived: Base {  
        let number: Int
    
        override init() {
            // super.init() // calling this here is not allowed!    
            number = 5
            super.init() // calling it here is ok :)
        }
    }
    

    Therefore, it is safe to call virtual methods in the base class initialiser. By the time it is called, all the derived class stored properties are guaranteed to be initialised.

    See also: Two-Phase Initialization in the language guide.