c++inheritanceprivate-memberspublic-members

C++ | Derived class is accessing private members of the base class rather than its own private members


sorry if this is an obvious one but I've searched around and I'm still unclear about how to solve this issue. Here is my code:

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

class PermMagnet {
public:
    string name;
    int ac_rating;
    int dc_rating;
    int mass_kg;
    int age;
    PermMagnet(){
        // default constructor
        name = "";
        ac_rating = 0; dc_rating = 0;
        mass_kg = 0; age = 0;
    }
    PermMagnet(string c_name, int c_ac_rating, int c_dc_rating, int c_mass_kg, int c_age){
        // parameterised constructor
        name = c_name;
        ac_rating = c_ac_rating;
        dc_rating = c_dc_rating;
        mass_kg = c_mass_kg;
        age = c_age;
    }
    string get_owner(){
        return owner;
    }
    string get_classifier(){
        return classifier;
    }
    int get_coil_count(){
        return coil_num;
    }
protected:
    string location = "facility hall";
private:
    string owner = "Unspecified Staff";
    string classifier = "MAG-DP-";
    const int coil_num = 2;
};

class ElecMagnet : public PermMagnet {
public:
    // inherit base class constructors
    using PermMagnet::PermMagnet;

    string get_location(){
        return location;
    }

private:
    string owner = "Specified Staff";
    string classifier = "MAG-QD-";
    const int coil_num = 4;
};

int main() {

    // Create object using default constructor
    PermMagnet perm1;
    cout << "'perm1' age: " << perm1.age << endl;

    // Create object using parameterised constructor
    PermMagnet perm2("PermMagnet 2", 380, 400, 1500, 35);
    cout << "'perm2' age: " << perm2.age << " | 'perm2' name: " << perm2.name << endl;
    cout << "Owner of 'perm2': " << perm2.get_owner() << endl;
    cout << "Upper current bound of 'perm2': " << perm2.get_current_limit("upper") << "A" << endl;
    cout << "Number of coils in 'perm2': " << perm2.get_coil_count() << endl << endl;

    // Create a ElecMagnet (derived class) object
    ElecMagnet elec1("ElecMagnet 1", 170, 200, 850, 27);
    cout << elec1.get_classifier() << endl;
    cout << elec1.get_coil_count() << endl;

    return 0;
}

The output of this code is:

'perm1' age: 0
'perm2' age: 35 | 'perm2' name: PermMaget 2
Owner of 'perm2': Unspecified Staff
Upper current bound of 'perm2': 780A
Number of coils in 'perm2': 2

MAG-DP-
2

Process finished with exit code 0

As you can see, I want "owner", "classifier" and "coil_num" to be private members that cannot be changed by the user. These also vary depending on the class in question.

The problem:

The problem is the last two lines of the output. When the derived class (ElecMagnet) inherits the public functions that return these members, it returns the members of the base class; not it's own. You can see this because it returns the "classifier" and "coil_num" of the PermMagnet class.

Does anyone know why the derived class is behaving this way? Shouldn't it access its own private members rather than the base?


Solution

  • Inheritance uses an "is a" relationship. Meaning, you should be able to say that a child object "is a" base object. If you cannot say this, you shouldn't be using inheritance.

    What this means for the building of a derived object is that it is a composite, containing your base object, and whatever extra bits have been added through inheritance.

    You called a base object function, which means it will access base object data. Repeating the data in the derived class obviously doesn't work, and is a bad practice. If you need the same data, use it and don't repeat it.

    You do this by having your derived class constructors always call base class constructors first in the initialization section. You don't use the initialization section at all, this can lead to a lot of inefficiencies and eventually errors.

    Random bits: All class data should be private. The protected: section of a class holds whatever you want a child to have public access to, but don't want to make fully public. If you don't want getters to be publicly available, they can go there. I placed a couple constructors that exist solely for the sake of ElecMagnet here. using namespace std; is bad practice.

    The code:

    #include <iostream>
    #include <string>
    
    class PermMagnet {
     public:
      PermMagnet() = default;
      PermMagnet(std::string c_name, int c_ac_rating, int c_dc_rating,
                 int c_mass_kg, int c_age)
          : name(c_name),
            ac_rating(c_ac_rating),
            dc_rating(c_dc_rating),
            mass_kg(c_mass_kg),
            age(c_age) {}  // Use the initialization section
    
      std::string get_owner() const { return owner; }  // Mark getters as const
    
      std::string get_classifier() const { return classifier; }
    
      int get_coil_count() const { return coil_num; }
    
      std::string get_location() const { return location; }
    
      int get_age() const { return age; }
    
      std::string get_name() const { return name; }
    
     protected:
      PermMagnet(std::string owner, std::string classifier, int coilNum)
          : owner(owner), classifier(classifier), coil_num(coilNum) {}
    
      PermMagnet(std::string name, int ac_rating, int dc_rating, int mass_kg,
                 int age, std::string owner, std::string classifier, int coilNum)
          : name(name),
            ac_rating(ac_rating),
            dc_rating(dc_rating),
            mass_kg(mass_kg),
            age(age),
            owner(owner),
            classifier(classifier),
            coil_num(coilNum) {}
    
     private:
      std::string owner = "Unspecified Staff";
      std::string classifier = "MAG-DP-";
      const int coil_num = 2;  // const probably unnecessary here, but left it
      std::string location = "facility hall";
      std::string name = "";
      int ac_rating = 0;
      int dc_rating = 0;
      int mass_kg = 0;
      int age = 0;
    };
    
    class ElecMagnet : public PermMagnet {
     public:
      ElecMagnet() : PermMagnet("Specified Staff", "MAG-QD-", 4) {}
    
      ElecMagnet(std::string name, int ac_rating, int dc_rating, int mass_kg,
                 int age)
          : PermMagnet(name, ac_rating, dc_rating, mass_kg, age, "Specified Staff",
                       "MAG-QD-", 4) {}
    
      // NO NEED FOR REPETITIVE PRIVATE SECTION
    };
    
    int main() {
      // Create object using default constructor
      PermMagnet perm1;
      std::cout << "'perm1' age: " << perm1.get_age()
                << '\n';  // Prefer '\n' over std::endl
    
      // Create object using parameterised constructor
      PermMagnet perm2("PermMagnet 2", 380, 400, 1500, 35);
      std::cout << "'perm2' age: " << perm2.get_age()
                << " | 'perm2' name: " << perm2.get_name() << '\n';
      std::cout << "Owner of 'perm2': " << perm2.get_owner() << '\n';
      std::cout << "Number of coils in 'perm2': " << perm2.get_coil_count()
                << "\n\n";
    
      // Create a ElecMagnet (derived class) object
      ElecMagnet elec1("ElecMagnet 1", 170, 200, 850, 27);
      std::cout << elec1.get_classifier() << '\n';
      std::cout << elec1.get_coil_count() << '\n';
    
      return 0;
    }
    

    Note that the ElecMagnet class is currently now just a couple constructors, and your main function behaves the way that's expected.