Search code examples
c++inheritanceinitialization-list

Subclass member variable as argument for main-class constructor in initialization list = crash?


Very new to programming so forgive me for maybe not seeing something obvious.

Basically I just want to know why all three codes do compile, but the resulting executables CRASH in cases TWO and THREE (I marked the differences with comments)

ONE - compiles

#include <iostream>
#include <string>
using namespace std;

string testrace = "dog"; //defining it only globally

class Attributes {
    public:
    Attributes (string race){
        if (race == "human"){
            intelligence = 10;}
        else if (race == "dog"){
            intelligence = 4;}
    }
    int intelligence;
};

class Dalmatian: public Attributes{
    public:
        // but NOT locally
        Dalmatian (): Attributes{testrace} { //using it as an argument
            cout << "do i even arrive here" << endl;
        }
};

int main() {
    Dalmatian bob;
    cout << bob.intelligence << endl;
}

TWO - crashes

#include <iostream>
#include <string>
using namespace std;


class Attributes {
    public:
    Attributes (string race){
        if (race == "human"){
            intelligence = 10;}
        else if (race == "dog"){
            intelligence = 4;}
    }
    int intelligence;
};

class Dalmatian: public Attributes{
    public:
        string testrace = "dog"; //only defining it locally
        Dalmatian (): Attributes{testrace} { //using it as argument
            cout << "do i even arrive here" << endl;
        }
};

int main() {
    Dalmatian bob;
    cout << bob.intelligence << endl;
}

THREE - crashes

#include <iostream>
#include <string>
using namespace std;

string testrace = "dog"; //defining it globally

class Attributes {
    public:
    Attributes (string race){
        if (race == "human"){
            intelligence = 10;}
        else if (race == "dog"){
            intelligence = 4;}
    }
    int intelligence;
};

class Dalmatian: public Attributes{
    public:
        string testrace = "dog"; // AND locally
        Dalmatian (): Attributes{testrace} { //using it as argument
            cout << "do i even arrive here" << endl;
        }
};

int main() {
    Dalmatian bob;
    cout << bob.intelligence << endl;
}

What I am looking for, of course, is a working alternative to example TWO. However I am also interested in an explanation why all of the three pieces of code will compile fine, but executables resulting from 2 and 3 will crash.

EDIT: I do know that examples ONE and THREE don't make sense, I used them for demonstrational purposes. (Also fixed my wording, compiler did fine, executable crashed);

EDIT2: I do, of course, realize that I could just replace "testrace" with ""dog"", but for easier transferability to other subclasses, I would prefer a solution that lets me use a variable argument for Attributes(), that I can vary depending on the subclass, that is invoking the main class.


Solution

  • First off when you have

    ...
    string testrace = "dog";
    Dalmatian (): Attributes{testrace}
    ... 
    

    testrace will hide the global testrace as class members supersede global variables when you are in class scope. This means both example two and three use the same variable, the class member variable.

    The reason two and three crash because you are trying to use a variable that has no been constructed before. When you get to

    Dalmatian (): Attributes{testrace}
    

    testrace has not yet been constructed. Even though you have string testrace = "dog"; in the class body that initialization doesn't happen until after Attributes{testrace} is called. So Attributes (string race) gets a uninitialized string and using it is undefined behavior and also causes your crash.