How do C++ class members get initialized if I don't do it explicitly?

In lieu of explicit initialization, initialization of members in classes works identically to initialization of local variables in functions.

For objects, their default constructor is called. For example, for std::string, the default constructor sets it to an empty string. If the object's class does not have a default constructor, it will be a compile error if you do not explicitly initialize it.

For primitive types (pointers, ints, etc), they are not initialized -- they contain whatever arbitrary junk happened to be at that memory location previously.

For references (e.g. std::string&), it is illegal not to initialize them, and your compiler will complain and refuse to compile such code. References must always be initialized.

So, in your specific case, if they are not explicitly initialized:

    int *ptr;  // Contains junk
    string name;  // Empty string
    string *pname;  // Contains junk
    string &rname;  // Compile error
    const string &crname;  // Compile error
    int age;  // Contains junk

First, let me explain what a mem-initializer-list is. A mem-initializer-list is a comma-separated list of mem-initializers, where each mem-initializer is a member name followed by (, followed by an expression-list, followed by a ). The expression-list is how the member is constructed. For example, in

static const char s_str[] = "bodacydo";
class Example
{
private:
    int *ptr;
    string name;
    string *pname;
    string &rname;
    const string &crname;
    int age;

public:
    Example()
        : name(s_str, s_str + 8), rname(name), crname(name), age(-4)
    {
    }
};

the mem-initializer-list of the user-supplied, no-arguments constructor is name(s_str, s_str + 8), rname(name), crname(name), age(-4). This mem-initializer-list means that the name member is initialized by the std::string constructor that takes two input iterators, the rname member is initialized with a reference to name, the crname member is initialized with a const-reference to name, and the age member is initialized with the value -4.

Each constructor has its own mem-initializer-list, and members can only be initialized in a prescribed order (basically the order in which the members are declared in the class). Thus, the members of Example can only be initialized in the order: ptr, name, pname, rname, crname, and age.

When you do not specify a mem-initializer of a member, the C++ standard says:

If the entity is a nonstatic data member ... of class type ..., the entity is default-initialized (8.5). ... Otherwise, the entity is not initialized.

Here, because name is a nonstatic data member of class type, it is default-initialized if no initializer for name was specified in the mem-initializer-list. All other members of Example do not have class type, so they are not initialized.

When the standard says that they are not initialized, this means that they can have any value. Thus, because the above code did not initialize pname, it could be anything.

Note that you still have to follow other rules, such as the rule that references must always be initialized. It is a compiler error to not initialize references.