Nowibananatzki
Nowibananatzki

Reputation: 786

Confusion about inheritance & access level

I've got some confusions on the access level in C++ inheritance and more generally how I should design a C++ class.

class Car
{
public:

private:
    string brandName;
}

class Sedan: public Car
{
public:
    // this function needs to know the Sedan's brandName
    void someFun()
    {
        brandName = "sss"; // error!!!    
    }
private:
}

In the above example the Car as a base class has a private member "brandName", and the Sedan as a derived class from Car inherits that member "brandName". As explained in Effective C++, public inheritance makes an "is-a" relationship, which means Sedan is a Car, now can I say that Sedan has a "brandName"?. If the answer is yes, why couldn't Sedan access to its own attribute in someFun?

I know the solution is to change brandName's access level to protected, my confusion is that what features or conditions make a variable member to which access level, in other word how I decide which access level should a given member be attached to?

I'd also like it if you'd recommend any book or article elaborating on this topic.

Upvotes: 1

Views: 401

Answers (6)

juanchopanza
juanchopanza

Reputation: 227370

can I say that Sedan has a "brandName"?.

Yes, it certainly has an object of type string called Car::brandName (whether this constitutes an OOP "has-a" relationship isn't entirely clear to me, but probably not: see comments)

If the answer is yes, why couldn't Sedan access to its own attribute in someFun?

Simple because brandName is private. Those are the rules of the language. If you want to give a derived class access to a non-public data member, you can make it protected.

I'd also like it if you'd recommend any book or article elaborating on this topic.

There is a list of books here. Perhaps "The C++ Programming Language" would be good for explaining this particular aspect.

Upvotes: 2

Werner Erasmus
Werner Erasmus

Reputation: 4076

I've got some confusions on the access level in C++ inheritance and more generally how I should design a C++ class.

C++ has three access level specifiers that can be used when inheriting:

  • private, protected and public

class Derived : public Base

Is called public inheritance and represents IsA relationship. When inheriting publicly, public members of the base remain public in Derived, protected members remain protected in Derived and private members are private (and not accessible from Derived).

class Derived : protected Base Is called protected inheritance. This kind of inheritance is rare, and would be used if you don't want to expose the public part of Base when accessed as derived (or through the interface of Derived). In this kind of inheritance the public members in base become protected in derived.

Also see the C++ FAQs

class Derived : private Base

Is called private inheritance. This kind of inheritance can be used to simulate containment (Herb Sutter - Uses and abuses of inheritance). Here public members in Base become private when accessed through the interface of derived.

It can also be noted that protected and private inheritance does not represent the classic IsA relationship. The following polymorphic behaviour is only possible when inheriting publicly:

Derived d;
Base* b = &d;

However, for private inheritance polymorphic behaviour is possible in the first derived class (but not in subsequent).

struct Base{};
struct Derived : private Base
{
  Derived()
  {
    //IsA holds within member functions of Derived, but not
    // outside
    Base* b = this; 
  }
};
struct Derived2 : public Derived
{
  Derived2()
  {
    Base* b = this; //Fails to compile...inaccessible base
  }
};
int main()
{
  Derived d;
  Base* b = &d; //Fails to compile
}

For protected inheritance polymorphic behaviour is possible in the all subsequent derived classes (Therefore code in constructor of Derived2 here above would compile), but not outside of class member functions.

Herb Sutter comments on reasons for using non public inheritance in Uses and abuses.

Finally, a comment concerning your example above: Usually Car would be abstract (an interface, consisting only of pure virtual functions), and therefore it would not contain any data members, but leave it open to the implementation. This is a guideline concerning inheritance that I've heard somewhere Sutter - Exceptional C++:

Inherit publicly in order to be reused by code that uses base classes polymorphically.

Your example would/could become:

struct Car
{
  //Note: const used as brandName should not modify logical state.
  virtual const std::string& brandName() const = 0;

  //...virtual ~Car(), amongst others, depending
  // on whether you intend to be deleted via this interface...
};

// Note: public inheritance implied by struct
struct CarFromFile: /*public*/ Car
{
  CarFromFile( std::ifstream& file )
  : brandName_( strFrom( file ) ),
    manufacturer_( strFrom( file )
  {
  }
  virtual const std::string& brandName() const{ return brandName_; }
  private:
    std::string strFrom( std::ifstream& file )
    {
      std::string value;
      if( file >> value ) { return value; }
      throw std::runtime_error( "Invalid file format!" );
    }
    std::string brandName_;
    std::string manufacturer_;
    //etc...
};

The fact that you make the accessor abstract, allows freedom from the perspective of the implementer of derived, and defines the service required by the client of base, independent of how the actual data looks.

Upvotes: 1

Arne Mertz
Arne Mertz

Reputation: 24576

now can I say that Sedan has a "brandName"?

No, neither has Car. The brandName is an implementatin detail, private, hidden from anyone else and does not contribute to the class' public interface, which is what makes the nature of a Car. So while technically there is a brandName somewhere in a Car and thus in a Sedan, from a pure OO view that does not matter. Similar to "Is-A"-relationships that are expressed only by public inheritance and not by private inheritance, "Has-A"-relationships in the OO point of view are only present if the composition or assoctiation is publicly accessible/visible (mostly via getters/setters).

juanchopanza's answer and the comments to it have lead me to sneak a little around the web, trying to find resources about Is-A and Has-An relationships. this transcription seems to indicate that the two terms did not originate from OO thoughts. In fact, OO literature seems to cite the Liskov Substitution Principle instead of using "Is-A" relationships. It lays stress on encapsulation and opaque objects, so it concerns mostly with public interfaces of classes and how their behavior affect associated objects. While a "Has-A" relationship can be association, it also can be aggregation. In this case, since the brandName is not an external, associated object (in C++ we would express that fact with a pointer or reference member), it is an aggregation-"Has-A", hidden in the opaque Car and therefore not of interest for OO (and the above answer is left semantically valid).

I know the solution is to change brandName's access level to protected

NO! brandName's access level has to remain private, so that Car has the complete control over what happens to it. If you want to access it in derived classes, provide protected getters and/or setters.

I'd also like it if you'd recommend any book or article elaborating on this topic.

Any book about object oriented design should do. If you read through two or more of them, you will get a good idea of what to do and what not to do.

Upvotes: 3

No Idea For Name
No Idea For Name

Reputation: 11577

Public members are free to all to use.

Private members are for the use of only this current class, and no one else. It is used mostly for inner members of the class to use and save for it's own purposes or to provide a method to access to them.

Protected members are accessible for inherited classes, just as your example shows - brandName is for the use of inherit classes, so it should be protected.

Upvotes: 1

Zeeshan
Zeeshan

Reputation: 3024

you have hidder brandName from the outer world, by making it private. Make it protected so that children may inherit it. To understand inheritance rules regarding specifiers, go to HERE

Upvotes: 1

jxh
jxh

Reputation: 70382

The classifications for public, protected and private are intended to allow one to separate publicly available interfaces from internal implementation details. Thus, things that are not public are inaccessible, forcing users of the class to use the public interfaces provided.

If something is private, then even though inheritance will make the member a part of the derived class, the derivation is not allowed to access it. If it is desirable to allow a derived class access to interfaces and members that are not part of the public interface, then that is exactly when to use protected.

Upvotes: 0

Related Questions