Наследование в C ++

Опубликовано: 7 Января, 2022

Способность класса извлекать свойства и характеристики из другого класса называется наследованием . Наследование - одна из наиболее важных функций объектно-ориентированного программирования.
Подкласс: класс, наследующий свойства от другого класса, называется подклассом или производным классом.
Суперкласс: класс, свойства которого наследуются подклассом, называется базовым классом или суперклассом.
Статья разбита на следующие подтемы:

  1. Зачем и когда использовать наследование?
  2. Способы наследования
  3. Типы наследования

Зачем и когда использовать наследование?

Рассмотрим группу автомобилей. Вам нужно создать классы для автобусов, автомобилей и грузовиков. Методы fuelAmount (), capacity (), applyBrakes () будут одинаковыми для всех трех классов. Если мы создадим эти классы, избегая наследования, тогда мы должны написать все эти функции в каждом из трех классов, как показано на рисунке ниже:

Вы можете ясно видеть, что приведенный выше процесс приводит к дублированию одного и того же кода 3 раза. Это увеличивает вероятность ошибки и избыточности данных. Чтобы избежать такой ситуации, используется наследование. Если мы создадим класс Vehicle и напишем в него эти три функции и унаследуем остальные классы от класса транспортного средства, то мы сможем просто избежать дублирования данных и увеличить возможность повторного использования. Посмотрите на диаграмму ниже, на которой три класса унаследованы от класса транспортного средства:


Используя наследование, мы должны писать функции только один раз, а не трижды, поскольку мы унаследовали остальные три класса от базового класса (Vehicle).
Реализация наследования в C ++ : для создания подкласса, унаследованного от базового класса, мы должны следовать синтаксису ниже.
Синтаксис :

 class subclass_name: access_mode base_class_name
{
  // тело подкласса
};

Here, subclass_name is the name of the sub class, access_mode is the mode in which you want to inherit this sub class for example: public, private etc. and base_class_name is the name of the base class from which you want to inherit the sub class. 
Note: A derived class doesn’t inherit access to private data members. However, it does inherit a full parent object, which contains any private members which that class declares.

CPP

// C++ program to demonstrate implementation
// of Inheritance
  
#include <bits/stdc++.h>
using namespace std;
 
//Base class
class Parent
{
    public:
      int id_p;
};
  
// Sub class inheriting from Base Class(Parent)
class Child : public Parent
{
    public:
      int id_c;
};
 
//main function
int main()
   {
      
        Child obj1;
          
        // An object of class child has all data members
        // and member functions of class parent
        obj1.id_c = 7;
        obj1.id_p = 91;
        cout << "Child id is " <<  obj1.id_c << endl;
        cout << "Parent id is " <<  obj1.id_p << endl;
         
        return 0;
   }

Выход:

 Идентификатор ребенка - 7
Родительский идентификатор 91

В приведенной выше программе класс «Дочерний» публично наследуется от класса «Родитель», поэтому открытые члены данных класса «Родитель» также будут унаследованы классом «Дочерний».
Способы наследования

  1. Открытый режим : если мы производим подкласс из общедоступного базового класса. Тогда открытый член базового класса станет общедоступным в производном классе, а защищенные члены базового класса станут защищенными в производном классе.
  2. Защищенный режим : если мы производим подкласс от защищенного базового класса. Тогда как открытый член, так и защищенные члены базового класса станут защищенными в производном классе.
  3. Частный режим : если мы производим подкласс из частного базового класса. Тогда как открытый, так и защищенный члены базового класса станут частными в производном классе.

Note : The private members in the base class cannot be directly accessed in the derived class, while protected members can be directly accessed. For example, Classes B, C and D all contain the variables x, y and z in below example. It is just question of access.  

CPP

// C++ Implementation to show that a derived class
// doesn’t inherit access to private data members.
// However, it does inherit a full parent object
class A
{
public:
    int x;
protected:
    int y;
private:
    int z;
};
 
class B : public A
{
    // x is public
    // y is protected
    // z is not accessible from B
};
 
class C : protected A
{
    // x is protected
    // y is protected
    // z is not accessible from C
};
 
class D : private A    // "private" is default for classes
{
    // x is private
    // y is private
    // z is not accessible from D
};

В приведенной ниже таблице резюмируются указанные выше три режима и показаны спецификаторы доступа для членов базового класса в подклассе, когда они производятся в общедоступном, защищенном и частном режимах:

Типы наследования в C ++

1. Одиночное наследование . При одиночном наследовании классу разрешено наследование только от одного класса. т.е. один подкласс наследуется только одним базовым классом.

Syntax

class subclass_name : access_mode base_class
{
  //body of subclass
};

CPP

// C++ program to explain
// Single inheritance
#include <iostream>
using namespace std;
 
// base class
class Vehicle {
  public:
    Vehicle()
    {
      cout << "This is a Vehicle" << endl;
    }
};
 
// sub class derived from a single base classes
class Car: public Vehicle{
 
};
 
// main function
int main()
{  
    // creating object of sub class will
    // invoke the constructor of base classes
    Car obj;
    return 0;
}

Выход:

 Это автомобиль

2. Множественное наследование. Множественное наследование - это функция C ++, в которой класс может наследовать от нескольких классов. т.е. один подкласс наследуется более чем от одного базового класса .

Синтаксис :

 class subclass_name: access_mode base_class1, access_mode base_class2, ....
{
  // тело подкласса
};

Here, the number of base classes will be separated by a comma (‘, ‘) and access mode for every base class must be specified. 

CPP

// C++ program to explain
// multiple inheritance
#include <iostream>
using namespace std;
 
// first base class
class Vehicle {
  public:
    Vehicle()
    {
      cout << "This is a Vehicle" << endl;
    }
};
 
// second base class
class FourWheeler {
  public:
    FourWheeler()
    {
      cout << "This is a 4 wheeler Vehicle" << endl;
    }
};
 
// sub class derived from two base classes
class Car: public Vehicle, public FourWheeler {
 
};
 
// main function
int main()
{  
    // creating object of sub class will
    // invoke the constructor of base classes
    Car obj;
    return 0;
}

Выход:

 Это автомобиль
Это 4-х колесный автомобиль.

Посетите эту ссылку, чтобы подробнее узнать о множественном наследовании.
3. Многоуровневое наследование . В этом типе наследования производный класс создается из другого производного класса.


CPP

// C++ program to implement
// Multilevel Inheritance
#include <iostream>
using namespace std;
 
// base class
class Vehicle
{
  public:
    Vehicle()
    {
      cout << "This is a Vehicle" << endl;
    }
};
 
// first sub_class derived from class vehicle
class fourWheeler: public Vehicle
public:
    fourWheeler()
    {
      cout<<"Objects with 4 wheels are vehicles"<<endl;
    }
};
// sub class derived from the derived base class fourWheeler
class Car: public fourWheeler{
   public:
     car()
     {
       cout<<"Car has 4 Wheels"<<endl;
     }
};
 
// main function
int main()
{  
    //creating object of sub class will
    //invoke the constructor of base classes
    Car obj;
    return 0;
}

Выход:

 Это автомобиль
Объекты с 4 колесами являются транспортными средствами.
Автомобиль имеет 4 колеса

4. Иерархическое наследование . В этом типе наследования более одного подкласса наследуется от одного базового класса. т.е. из одного базового класса создается более одного производного класса.


CPP

// C++ program to implement
// Hierarchical Inheritance
#include <iostream>
using namespace std;
 
// base class
class Vehicle
{
  public:
    Vehicle()
    {
      cout << "This is a Vehicle" << endl;
    }
};
 
 
// first sub class
class Car: public Vehicle
{
 
};
 
// second sub class
class Bus: public Vehicle
{
     
};
 
// main function
int main()
{  
    // creating object of sub class will
    // invoke the constructor of base class
    Car obj1;
    Bus obj2;
    return 0;
}

Выход:

 Это автомобиль
Это автомобиль

5. Гибридное (виртуальное) наследование . Гибридное наследование реализуется путем объединения более одного типа наследования. Например: сочетание иерархического наследования и множественного наследования.
На изображении ниже показано сочетание иерархического и множественного наследования:


CPP

// C++ program for Hybrid Inheritance
 
#include <iostream>
using namespace std;
 
// base class
class Vehicle
{
  public:
    Vehicle()
    {
      cout << "This is a Vehicle" << endl;
    }
};
 
//base class
class Fare
{
    public:
    Fare()
    {
        cout<<"Fare of Vehicle ";
    }
};
 
// first sub class
class Car: public Vehicle
{
 
};
 
// second sub class
class Bus: public Vehicle, public Fare
{
     
};
 
// main function
int main()
{  
    // creating object of sub class will
    // invoke the constructor of base class
    Bus obj2;
    return 0;
}

Выход:

 Это автомобиль
Стоимость транспортного средства

6. Частный случай гибридного наследования: Многопутевое наследование :
Производный класс с двумя базовыми классами, и эти два базовых класса имеют один общий базовый класс, называется многопутевым наследованием. В этом типе наследования может возникнуть двусмысленность.

Consider the following program:  

CPP

// C++ program demonstrating ambiguity in Multipath
// Inheritance
 
#include <conio.h>
#include <iostream.h>
class ClassA {
public:
    int a;
};
 
class ClassB : public ClassA {
public:
    int b;
};
class ClassC : public ClassA {
public:
    int c;
};
 
class ClassD : public ClassB, public ClassC {
public:
    int d;
};
 
void main()
{
 
    ClassD obj;
 
    // obj.a = 10;                   //Statement 1, Error
    // obj.a = 100;                 //Statement 2, Error
 
    obj.ClassB::a = 10; // Statement 3
    obj.ClassC::a = 100; // Statement 4
 
    obj.b = 20;
    obj.c = 30;
    obj.d = 40;
 
    cout << " A from ClassB  : " << obj.ClassB::a;
    cout << " A from ClassC  : " << obj.ClassC::a;
 
    cout << " B : " << obj.b;
    cout << " C : " << obj.c;
    cout << " D : " << obj.d;
}

Выход:

 A из класса B: 10
A из класса C: 100
А: 20
С: 30
Д: 40

In the above example, both ClassB & ClassC inherit ClassA, they both have single copy of ClassA. However ClassD inherit both ClassB & ClassC, therefore ClassD have two copies of ClassA, one from ClassB and another from ClassC. 
If we need to access the data member a of ClassA through the object of ClassD, we must specify the path from which a will be accessed, whether it is from ClassB or ClassC, bco’z compiler can’t differentiate between two copies of ClassA in ClassD.
There are 2 ways to avoid this ambiguity: 
Avoiding ambiguity using scope resolution operator: 
Using scope resolution operator we can manually specify the path from which data member a will be accessed, as shown in statement 3 and 4, in the above example. 

CPP

obj.ClassB::a = 10;        //Statement 3
obj.ClassC::a = 100;      //Statement 4

Note : Still, there are two copies of ClassA in ClassD.
Avoiding ambiguity using virtual base class: 

CPP

#include<iostream.h>
     #include<conio.h>
 
     class ClassA
     {
            public:
            int a;
     };
 
     class ClassB : virtual public ClassA
     {
            public:
            int b;
     };
     class ClassC : virtual public ClassA
     {
            public:
            int c;
     };
 
     class ClassD : public ClassB, public ClassC
     {
            public:
            int d;
     };
 
     void main()
     {
 
            ClassD obj;
 
            obj.a = 10;        //Statement 3
            obj.a = 100;      //Statement 4
 
            obj.b = 20;
            obj.c = 30;
            obj.d = 40;
 
            cout<< " A : "<< obj.a;
            cout<< " B : "<< obj.b;
            cout<< " C : "<< obj.c;
            cout<< " D : "<< obj.d;
 
     }

Выход:

 А: 100
А: 20
С: 30
Д: 40

Согласно приведенному выше примеру ClassD имеет только одну копию ClassA, поэтому оператор 4 перезапишет значение a, заданное в операторе 3.
Автор статьи - Харш Агарвал. Если вам нравится GeeksforGeeks, и вы хотели бы внести свой вклад, вы также можете написать статью на сайте deposit.geeksforgeeks.org или отправить свою статью по электронной почте: grant@geeksforgeeks.org. Посмотрите, как ваша статья появляется на главной странице GeeksforGeeks, и помогите другим гикам.

Хотите узнать о лучших видео и практических задачах, ознакомьтесь с базовым курсом C ++ для базового и продвинутого уровня C ++ и курсом C ++ STL для базового уровня плюс STL. Чтобы завершить подготовку от изучения языка к DS Algo и многому другому, см. Полный курс подготовки к собеседованию .