Short answer
This is an empty-body constructor and a member initialization list that initializes two data members with a value of 0 .
i.e.
class-name([ ctor-args ]) [ : member-init-list ] { [ ctor-body ] }
where member-init-list is member-name(args) [, member-name(args) [, ... ] ] .
(Note: non-actual C ++ lexical grammar constructs)
Long answer
Background
Take the following class definitions:
struct Base {}; struct Derived : Base {};
You may already know that Derived comes from Base .
You may also already know that Derived and Base have a synthesized (implicitly declared) default constructor (no arguments). Derived constructor implicitly / automatically calls Base .
Now add a basic member function:
struct Derived : Base { void foo() {} };
I declared and defined this member function foo ; his body is empty, so nothing happens when you call him. This is pretty pointless, but it is absolutely true.
Now, instead, create your own constructor:
struct Derived : Base { Derived() {} };
It looks more familiar. This is still an [special] function with an empty body, and the Base constructor is still implicitly called. We have not changed that.
Data Item Processing
Add some data elements and set their value in our constructor:
struct Derived : Base { Derived() { x = 0; y = 0; } int x, y; };
Both objects x and y will have a value of 0 after building the object. This is still fundamental C ++.
But you may not know that you are not initializing these data members. You simply assign them after the possibility of their initialization.
In fact, built-in data elements are implicitly initialized, so let's choose the best example:
struct Derived : Base { Derived() { x = ""; y = ""; } std::string x, y; };
x and y implicitly initialized before the code in the constructor body is executed. When the constructor body starts to work, all the elements that will be initialized are already initialized, and the base constructor was implicitly called.
We can intercept this behavior and provide our own initialization values ββby writing a list of member initiators in the place where I wrote /* HERE */ in the previous snippet. Using a list of member initializers, this snippet looks like this:
struct Derived : Base { Derived() : x(""), y("") { x = ""; y = ""; } std::string x, y; };
Wow! OK; now we initialize the strings "" and then assign them the same empty value in the body of the constructor. We can get rid of these appointments, then:
struct Derived : Base { Derived() : x(""), y("") {} std::string x, y; };
Now the constructor body is empty, but the constructor is still doing things. It implicitly calls the base constructor and explicitly initializes the data members x and y an empty string.
And, since std::string has a default constructor that does the same, we can write briefly:
struct Derived : Base { Derived() : x(), y() {} std::string x, y; };
Returning to the original example, this is also true for objects of built-in types. Consider two pointers:
struct Derived : Base { Derived() : next(0), prev(0) {} Derived* next; Derived* prev; };
Base Class Handling
And as an added bonus, we can use the member initialization list to explicitly call the base constructor:
struct Derived : Base { Derived() : Base(), next(0), prev(0) {} Derived* next; Derived* prev; };
This is pretty pointless if the base constructor doesn't need some arguments:
struct Base { Base(int x) {} }; struct Derived : Base { Derived() : Base(0), next(0), prev(0) {} Derived* next; Derived* prev; };
I hope this was helpful.