Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Initialization in Modern C++ vs. Apple's Swift

DZone's Guide to

Initialization in Modern C++ vs. Apple's Swift

· Java Zone
Free Resource

Just released, a free O’Reilly book on Reactive Microsystems: The Evolution of Microservices at Scale. Brought to you in partnership with Lightbend.

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 paramaters 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 callsuper.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 _agewould 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.

Strategies and techniques for building scalable and resilient microservices to refactor a monolithic application step-by-step, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:

Published at DZone with permission of Michael Kennedy, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}