Time for another great guest blog post from Bradley Needham to follow up on his very popular Swift vs. C++ post. Enjoy!
Initialization in Modern C++ vs Apple’s Swift
A little while ago I wrote a short post comparing some of the basic features of Modern C++ and Apple’s Swift. It was far from comprehensive, basically only touching on the constructs mentioned in Michael Kennedy’s post Comparison of Python and Apple’s Swift Programming Language Syntax. However the post did generate enough interest that I decided to continue the comparison by looking at one of the differences I find interesting between the two languages, initialization. Initialization is very important in any language and both C++ and Swift have built in constructs and checks to help make sure that objects get completely initialized before they are used.
Initialization syntax
Let us start with the syntax of the two languages.
Objects are initialized in C++ through constructors. A constructor has no return value and the same name as the class.
// C++ class Person { public: Person() {} // Constructor };
In Swift objects are initialized through initializers. An initializer has no return value and its name is init.
// Swift public class Person { public init() {} // Initializer }
Invoking a constructor or an initializer is similar.
Note: I am only pointing out the syntax here, how the objects are being allocated is different.
// C++ Person p; // or auto p = Person();
// Swift var p = Person()
Constructors and initializers can be used to set initial values for fields.
// C++ class Person { int _age; public: Person() : _age(25) { } };
// Swift public class Person { var _age : Int public init() { _age = 25 } }
Fields can also be inline initialized.
// C++ class Person { int _age = 25; public: Person() { } };
// Swift public class Person { var _age : Int = 25 public init() { } }
Constructors and initializers can take parameters. Note: I am only showing that parameters can be passed, and not discussing implicit vs explicit parameters in Swift.
// C++ class Person { int _age; public: Person(int age) : _age(age) { } }; // invoke auto p = Person(25);
// Swift public class Person { var _age : Int public init(age : Int) { _age = age } } // invoke var p = Person(age: 25)
Constructors can invoke other constructors in the same class.
// C++ class Person { int _age; public: Person() : Person(25) { } // invoke the below constructor Person(int age) : _age(age) { } };
Initializers can invoke other initializers in the same class.
// Swift public class Person { var _age : Int public convenience init() { self.init(age: 25) // invoke the below initializer } public init(age : Int) { _age = age } }
Constructors and initializers can invoke their base class’s initializers or constructors.
// C++ class Person { int _age; public: Person(int age) : _age(age) { } };
class Student : public Person { int _id; public: Student(int age, int id) : Person(age), // invoke base constructor _id(id) {} };
// Swift public class Person { var _age : Int public convenience init() { self.init(age: 25) // invoke the below initializer } public init(age : Int) { _age = age } }
public class Student : Person { var _id : Int public init(age: Int, id: Int) { _id = id super.init(age) // invoke base constructor } }
Order of initialization
Now that we have seen the basics of the initialization syntax of C++ and Swift, let us get to the interesting part, order of initialization.
In C++ the order of construction is determined by the class declaration.
Objects are initialized from the top down in the inheritance heiarchy. Base classes first left to right then data members top to bottom. Given the following code:
// C++ // Given the following classes class Person { int _age; public: Person(int age) : _age(age) { // constructor code } };
class Student : public Person { int _id; public: Student(int age, int id) : Person(age), // invoke base constructor _id(id) { // constructor code } };
When an instance of a Student is created.
auto s = Student(25, 1001);
The constructor for Person will be executed first. It will initialize _age
and execute any code in the constructor body. Then _id
will be initialized and finally the the code in the Student constructor will be executed.
This is straight forward single-phase construction that works well in C++.
In Swift initialization is done in two-phases.
Two-phase initialization takes a different approach. Instead of completely initializing the base class protion of the object, i.e. initializing the data as well as executing the initialization code, before initializing the sub class protion, it separates the initialization of the data from the initialization code.
The first phase, initializes the data starting with the sub class fields and working its way up to the base class fields. The compilier will enforce this so that you cannot call super.init
before all sub class fields have an initialized value and you will not be able to invoke any methods on the sub class until super.init
finishes. The second phase then executes code that can now safely access any fields because they are guarenteed to be initialized.
// Swift // Given the following classes public class Person { var _age: Int public init(age: Int) { _age = age // initialization code } }
public class Student : Person { var _id: Int public init(age: Int, id: Int) { _id = id super.init(age) // initialization code } }
When an instance of a Student is created.
var s = Student(25, 1001);
First the field _id
would be initialized, then super.init
would be called, then _age
would be initialized. This would complete the first-phase and the second-phase would then execute any initialization code.
Swift uses two-phase initialization to prevent code from accessing the memory of an object before it has been initialized which might happen if you were to call a method (which is dynamically bound by default) from a base class initializer.
// Swift public class Person { var _age: Int public init(age: Int) { _age = age print() // dynamically bound } public func print() { println("age: \(_age)") } }
public class Student : Person { var _id: Int public init(age: Int, id: Int) { _id = id super.init(age) // invoke base initializer } override public func print() { super.print() println("id: \(_id)") } }
Because _id
is initialized before super.init
is invoked the following code:
var s = Student(age: 25, id: 1001)
Prints out:
age: 25 id: 1001
In C++ dynamic binding is not fully set up until construction is complete so the above example in C++ would result in the print function on Person being called instead of the print function on Student.
// C++ class Person { int _age; public: Person(int age) : _age(age) { print(); } virtual void print() const { std::cout << "age: " << _age << std::endl; } }; class Student : public Person { int _id; public: Student(int age, int id) : Person(age), // invoke base constructor _id(id) {} void print() const override { Person::print(); std::cout << "id: " << _id << std::endl; } };
In C++ _id
is not initialized before Person’s constructor is invoked but the v-table used for dynamic binding is also not fully set up so Person::print gets called and the following code:
auto s = Student(25, 1001);
Prints out:
age: 25
Note: In Java _id
would not be initialized and print would be dynamically bound allowing the access of uninitialized memory.
Two-phase initialization can be implemented in C++ and there are many discussions on the pros and cons of its use. This post is not concerned with addressing those. The intention of this post is to simply point out the difference between C++’s build in initialization mechanism and Swift’s.
