Example analysis of access control in C++ inheritance

  • 2020-04-02 02:37:27
  • OfStack

In this paper, the access control in C++ inheritance is discussed in depth. The specific contents are as follows:

In general, we think of a class as having two different types of users: regular users and implementers of the class. Among them, the code written by ordinary users USES the object of the class, which can only access the public (interface) members of the class. The implementer is responsible for writing the code for the members and friends of the class, who can access both the public and private parts of the class. If inheritance is considered further, there is a third type of user, a derived class. Derived classes can access public and protected members of the base class, but not private members of the base class.

Inheritance related points are as follows:

Most classes inherit from only one class, and this form of inheritance is called "single inheritance". This article focuses on single inheritance.
The object of a derived class contains parts inherited from the base class and parts defined by the derived class. Because derived classes contain base class parts, you can cast derived classes to base classes implicitly.
There is no implicit type conversion from a base class to a derived class.
(4). The automatic type conversion from derived classes to base classes is only valid for Pointers or references, and there is no type conversion between objects.
If the base class defines a static member, there are only unique instances of each static member, no matter how many derived classes are derived.
The keyword final can be used to prevent a class from being inherited, as provided in the new C++11 standard.

In addition, the reader needs to know about the previous article (link: #).

I. public, private and protected members

1. Access specifier

In C++, access to the members of a class is controlled by using the access specifiers public, protected, and private to control whether the members are accessible to ordinary users or derived classes:

public : members defined as public are accessible to regular users, implementers of classes, and derived classes. Public is often used to define the external interface of a class.

protected : the purpose of defining a protected member is to make it accessible to derived classes and not accessible to other users. So the implementers and derived classes of the class can access it, but not the regular users.

private : a member defined as private can only be accessed by the implementers (members and friends) of the class. The private section is often used to encapsulate (that is, hide) the implementation details of a class.


class People{ 
protected: 
  string name; 
}; 
 
class Student : public People{ 
public: 
  friend void Print(Student &s); 
  friend void Print(People &p); 
}; 
 
//True, you can access the protected members of the base class through the derived class object
void Print(Student &s){ s.name="Songlee"; cout<< s.name << endl; } 
//Error. The protected member of the base class cannot be accessed through the base class object
void Print(People &p){ p.name="Songlee"; cout<< p.name << endl; } 

It is important to note that members or friends of derived classes can only access protected members of the base class through derived class objects. Derived classes have no access privileges to protected members of a base class object.

2. Change the accessibility of members

Sometimes we need to change the access level of a name that the derived class inherits by using the using declaration:


class People{ 
protected: 
  string name; 
}; 
 
class Student : public People{ 
public: 
  using People::name; //Change the access to the inherited name member to public
}; 
 
int main() 
{ 
  Student me; 
  me.name = "SongLee";   //Now you can access the name
  cout << me.name << endl;  
  return 0; 
} 

By using the using declaration statement within the class, we can mark any accessible (non-private) member of the direct or indirect base class of the class and change its access rights.

Public, private, and protected inheritance

We notice that the access specifiers public, protected, and private are used in the derived list of the class, which represent different ways of inheritance:


class A : public B {  };   //Public inheritance
class A : private B {  };  //Private inheritance
class A : protected B {  }; //Protected inheritance

The access specifiers in the derived list of derived classes have no effect on whether members (and friends) of the derived classes can access members of their immediate base classes. Access to a base class member by a member of a derived class (and a friend) is only related to the access specifier in the base class.

So what does the access specifier in the derived list do?

The access specifier in the derived list controls the access of users of the derived class to members of the base class. The following is the change of access caused by different inheritance methods:

Public inheritance : if the inheritance is public, the member will follow its original access specifier. The public, protected, and private attributes in the parent class do not change in the subclass.

Protected inheritance : access higher than protected will be protected. The public property in the parent class is protected in the subclass, and the protected and private properties in the parent class are protected in the subclass.

Private inheritance : access higher than private becomes private. That is, all three access properties in the parent class become private in the subclass.


class A {  //The base class
public: 
  string A_public;   //Members of the public
protected: 
  string A_protected;  //Protected member
}; 
 
class B : private A {  //Private inheritance
public: 
  B(){ A_public="public"; A_protected="protected"; }; 
}; 
 
int main() 
{ 
  B b;  //Object access through B
  cout << b.A_public <<" "<< b.A_protected << endl;  //Error because it is private inheritance
  return 0; 
}

If we don't use access specifiers in the derived list, the struct keyword defaults to public inheritance, and the class keyword defaults to private inheritance. However, it is recommended that the access specifier be written explicitly when inheritance occurs.

In addition, Different ways of inheritance also affect the conversion of derived classes to base classes , assuming that Derive from Base:

1. User code can use the transformation from derived class to Base class only when Derive inherits from Base communally; If Derive inherits Base in a protected or private way, the user code cannot use the transformation.

2. Regardless of whether Derive inherits Base in any way, Derive's member functions and friends can use the conversion from derived classes to Base classes; A type cast from a derived class to its immediate base class is always accessible to members and friends of the derived class.

3. If Derive inherits Base in a public or protected way, members and friends of derived classes of Derive can use Derive to convert to Base; On the other hand, Derive cannot be used if the way it inherits Base is private.


Related articles: