Наследование классов c: C++ | Наследование

Содержание

Наследование. Урок 4 курса «Объектно-ориентированное программирование на Python»

Наследование – важная составляющая объектно-ориентированного программирования. Так или иначе мы уже сталкивались с ним, ведь объекты наследуют атрибуты своих классов. Однако обычно под наследованием в ООП понимается наличие классов и подклассов. Также их называют супер- или надклассами и классами, а также родительскими и дочерними классами.

Суть наследования здесь схожа с наследованием объектами от классов. Дочерние классы наследуют атрибуты родительских, а также могут переопределять атрибуты и добавлять свои.

Простое наследование методов родительского класса

В качестве примера рассмотрим два класса столов. Класс Table – родительский по отношению к DeskTable (письменные столы). Независимо от своего типа все столы имеют длину, ширину и высоту. Пусть для письменных столов также важна площадь поверхности. Общее вынесем в класс, частное – в подкласс.

Наследственная связь между классами устанавливается через подкласс. При определении дочернего после его имени в скобках указывается родительский.

class Table:
    def __init__(self, l, w, h):
        self.length = l
        self.width = w
        self.height = h
 
 
class DeskTable(Table):
    def square(self):
        return self.width * self.length
 
 
t1 = Table(1.5, 1.8, 0.75)
t2 = DeskTable(0.8, 0.6, 0.7)
print(t2.square())  # вывод: 0.48

В данном случае у класса DeskTable нет своего конструктора, поэтому он наследует его от родителя. При создании объектов передавать аргументы необходимо в обоих случаях. Попытка вызова DeskTable с пустыми скобками приведет к ошибке.

С другой стороны, экземпляры надкласса Table, согласно неким родственным связям, не наследуют метод square своего подкласса.

В этом смысле терминология «родительский и дочерний класс» не совсем верна. Наследование в ООП – это скорее аналог систематизации и классификации наподобие той, что есть в живой природе. Все млекопитающие имеют четырехкамерное сердце, но только носороги – рог.

Полное переопределение метода надкласса

Рассмотрим вариант программы с «цепочкой наследования». Пусть дочерний по отношению к Table класс DeskTable в свою очередь выступит родительским по отношению к ComputerTable (компьютерные столы):

class Table:
    def __init__(self, l, w, h):
        self.length = l
        self.width = w
        self.height = h
 
 
class DeskTable(Table):
    def square(self):
        return self.width * self.length
 
 
class ComputerTable(DeskTable):
    def square(self, monitor=0.0):
        return self.width * self.length - monitor
 
 
t3 = ComputerTable(0.8, 0.6, 0.7)
print(t3.square(0.3))  # вывод: 0.18

Допустим, по задумке разработчиков рабочая поверхность компьютерного стола может вычисляться за вычетом площади, которую занимает монитор. В результате метод square в ComputerTable имеет отличия.

Определив в дочернем классе метод, одноименный методу родительского, мы тем самым

переопределяем метод родительского класса. При вызове square на экземпляры ComputerTable будет вызываться метод из этого класса, а не из родительского класса DeskTable.

В то же время ComputerTable наследует конструктор класса от своей «бабушки» – класса Table.

Дополнение, оно же расширение, метода

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

Пусть имеется подкласс кухонных столов, для которых необходимо еще одно поле – количество посадочных мест. Из-за этой детали мы вынуждены в дочернем классе переопределить конструктор родительского.

class Table:
    def __init__(self, l, w, h):
        self.length = l
        self.width = w
        self.height = h
 
 
class KitchenTable(Table):
    def __init__(self, l, w, h, p):
        self. length = l
        self.width = w
        self.height = h
        self.places = p
 
 
t4 = KitchenTable(1.5, 2, 0.75, 6)

Поскольку существенная часть кода конструктора подкласса является такой же как в надклассе, правильнее будет вызвать метод другого класса, а не дублировать код:

class Table:
    def __init__(self, l, w, h):
        self.length = l
        self.width = w
        self.height = h
 
 
class KitchenTable(Table):
    def __init__(self, l, w, h, p):
        Table.__init__(self, l, w, h)
        self.places = p
 
 
t4 = KitchenTable(1.5, 2, 0.75, 6)

Здесь в теле конструктора KitchenTable мы вызываем метод __init__ через объект-класс Table, а не через объект-экземпляр. Вспомним, что в таких случаях метод вызывается как обычная функция (объект, к которому применяется метод, не передается в качестве первого аргумента). Поэтому в конструктор надкласса мы «вручную» передаем текущий экземпляр (self), записывая его перед остальными аргументами.

У кода выше есть небольшой недостаток. Нам ничего не мешает (при условии совпадения количества параметров) вызвать конструктор другого класса, а не только родительского, указав его имя вместо Table. Кроме того, имя надкласса может измениться, и тогда есть риск неправильных обращений к нему из дочерних классов.

В Python c целью улучшения так называемой обслуживаемости кода можно использовать встроенную в язык функцию super. Наиболее распространенным вариантом ее применения является вызов метода родительского класса из метода подкласса:

class KitchenTable(Table):
    def __init__(self, l, w, h, p):
        super().__init__(l, w, h)
        self.places = p

В данном случае аргумент self в скобках вызываемого родительского метода указывать явно не требуется.

Параметры со значениями по умолчанию у родительского класса

Рассмотрим случай, когда родительский класс имеет параметры со значениями по умолчанию, а дочерний – нет:

class Table:
    def __init__(self, l=1, w=1, h=1):
        self. length = l
        self.width = w
        self.height = h
 
 
class KitchenTable(Table):
    def __init__(self, p, l, w, h):
        Table.__init__(self, l, w, h)
        self.places = p

При таком определении классов можно создать экземпляр от Table без передачи аргументов для конструктора:

t = Table()

Можем ли мы создать экземпляр от KitchenTable, передав значение только для параметра p? Например, вот так:

k = KitchenTable(10)

Возможно ли, что p будет присвоено число 10, а l, w и h получат по единице от родительского класса? Невозможно, будет выброшено исключение по причине несоответствия количества переданных аргументов количеству требуемых конструктором:

...
    k = KitchenTable(10)
TypeError: __init__() missing 3 required
 positional arguments: 'l', 'w', and 'h'

Когда создается объект от дочернего класса, сначала вызывается его конструктор, если он есть. Интерпретатор еще не знает, что в теле этого конструктора будет вызван конструктор родительского класса. Ведь это не обязательно. Значит, если все параметры дочернего конструктора не имеют значений по умолчанию, при построении объекта все значения должны передаваться.

Поэтому, если требуется допустить создание объектов от дочернего класса без передачи аргументов, придется назначить значения по умолчанию также в конструкторе дочернего класса.

class Table:
    def __init__(self, l=1, w=1, h=1):
        self.length = l
        self.width = w
        self.height = h
 
 
class KitchenTable(Table):
    def __init__(self, l=1, w=1, h=0.7, p=4):
        Table.__init__(self, l, w, h)
        self.places = p

Параметр p, которого нет у родительского класса, мы делаем последним не просто так. Бывает, объекты разных родственных классов создаются или обрабатываются в одном цикле, то есть по одному алгоритму. При этом у них должны быть одинаковые «интерфейсы», то есть одинаковое количество передаваемых в конструктор аргументов.

Поэтому лучше, когда методы родственных классов принимают одинаковое число параметров. А если разное, то у «лишних» должны быть значения по-умолчанию, чтобы при вызове конструктора их можно было бы не передавать. Если такие параметры находятся еще и в конце, передачу аргументов для предстоящих параметров можно выполнять без ключей.

Другой вариант – отказаться от конструктора в дочернем классе, а значение для поля places устанавливать отдельным вызовом метода:

class Table:
    def __init__(self, l=1, w=1, h=1):
        self.length = l
        self.width = w
        self.height = h
 
 
class KitchenTable(Table):
    places = 4
 
    def set_places(self, p):
        self.places = p

Здесь у всех кухонных столов по-умолчанию будет 4 места. Если мы хотим изменить значение поля places, можем вызвать метод set_places(). Хотя в случае Python можем сделать это напрямую, присвоив полю. При этом у экземпляра появится собственное поле places.

k = KitchenTable()
k.places = 6

Поэтому метод set_places() в общем-то не нужен.

В любом случае произвольное количество мест будет устанавливаться не в конструкторе, а отдельно.

Если все же требуется указывать места при создании объекта, это можно сделать и в конструкторе родителя:

class Table:
    def __init__(self, l=1, w=1, h=1):
        self.length = l
        self.width = w
        self.height = h
        if isinstance(self, KitchenTable):
            p = int(input("Сколько мест: "))
            self.places = p

С помощью функции isinstance() проверяется, что создаваемый объект имеет тип KitchenTable. Если это так, то у него появляется поле places.

Мы не используем параметр p со значением по умолчанию в заголовке конструктора потому, что, если объектам других родственных классов он не нужен, не происходило путаницы и сложностей с документированием кода.

Практическая работа

Разработайте программу по следующему описанию.

В некой игре-стратегии есть солдаты и герои. У всех есть свойство, содержащее уникальный номер объекта, и свойство, в котором хранится принадлежность команде. У солдат есть метод «иду за героем», который в качестве аргумента принимает объект типа «герой».

У героев есть метод увеличения собственного уровня.

В основной ветке программы создается по одному герою для каждой команды. В цикле генерируются объекты-солдаты. Их принадлежность команде определяется случайно. Солдаты разных команд добавляются в разные списки.

Измеряется длина списков солдат противоборствующих команд и выводится на экран. У героя, принадлежащего команде с более длинным списком, увеличивается уровень.

Отправьте одного из солдат первого героя следовать за ним. Выведите на экран идентификационные номера этих двух юнитов.

Курс с примерами решений практических работ:
pdf-версия, android-приложение


Наследование — Kotlin

Для всех классов в Kotlin родительским суперклассом является класс Any. Он также является родительским классом для любого класса, в котором не указан какой-либо другой родительский класс.

class Example // Неявно наследуется от Any

У Any есть три метода: equals(), hashCode() и toString(). Эти методы определены для всех классов в Kotlin.

По умолчанию все классы в Kotlin имеют статус final, который блокирует возможность наследования. Чтобы сделать класс наследуемым, его нужно пометить ключевым словом open.

open class Base // Класс открыт для наследования

Для явного объявления суперкласса мы помещаем его имя за знаком двоеточия в оглавлении класса:

open class Base(p: Int)
class Derived(p: Int) : Base(p)

Если у класса есть основной конструктор, базовый тип может (и должен) быть проинициализирован там же, с использованием параметров основного конструктора.

Если у класса нет основного конструктора, тогда каждый последующий дополнительный конструктор должен включать в себя инициализацию базового типа с помощью ключевого слова super или давать отсылку на другой конструктор, который это делает. Примечательно, что любые дополнительные конструкторы могут ссылаться на разные конструкторы базового типа.

class MyView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

Переопределение методов класса

Kotlin требует явно указывать модификаторы и для членов, которые могут быть переопределены, и для самого переопределения.

open class Shape {
    open fun draw() { /*...*/ }
    fun fill() { /*...*/ }
}
class Circle() : Shape() {
    override fun draw() { /*...*/ }
}

Для Circle.draw() необходим модификатор override. В случае её отсутствия компилятор выдаст ошибку. Если у функции типа Shape.fill() нет модификатора open, объявление метода с такой же сигнатурой в производном классе невозможно, с override или без. Модификатор open не действует при добавлении к членам final класса (т.е. класса без модификатора open).

Член класса, помеченный override, является сам по себе open, т.е. он может быть переопределён в производных классах. Если вы хотите запретить возможность переопределения такого члена, используйте final.

open class Rectangle() : Shape() {
    final override fun draw() { /*...*/ }
}

Переопределение свойств класса

Переопределение свойств работает также, как и переопределение методов; все свойства, унаследованные от суперкласса, должны быть помечены ключевым словом override, а также должны иметь совместимый тип. Каждое объявленное свойство может быть переопределено свойством с инициализацией или свойством с get-методом.

open class Shape {
    open val vertexCount: Int = 0
}
class Rectangle : Shape() {
    override val vertexCount = 4
}

Вы также можете переопределить свойство val свойством var, но не наоборот. Это разрешено, поскольку свойство val объявляет get-метод, а при переопределении его как var дополнительно объявляется set-метод в производном классе.

Обратите внимание, что ключевое слово override может быть использовано в основном конструкторе класса как часть объявления свойства.

interface Shape {
    val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // Всегда имеет 4 вершины
class Polygon : Shape {
    override var vertexCount: Int = 0  // Может быть установлено любое количество
}

Порядок инициализации производного класса

При создании нового экземпляра класса в первую очередь выполняется инициализация базового класса (этому шагу предшествует только оценка аргументов, передаваемых в конструктор базового класса) и, таким образом, происходит до запуска логики инициализации производного класса.

open class Base(val name: String) {
    init { println("Инициализация класса Base") }
    open val size: Int = 
        name.length.also { println("Инициализация свойства size в класса Base: $it") }
}
class Derived(
    name: String,
    val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Аргументы, переданные в конструктор класса Base: $it") }) {
    init { println("Инициализация класса Derived") }
    override val size: Int =
        (super.size + lastName.length).also { println("Инициализация свойства size в классе Derived: $it") }
}
fun main() {
    println("Построение класса Derived(\"hello\", \"world\")")
    Derived("hello", "world")
}

Это означает, что свойства, объявленные или переопределенные в производном классе, не инициализированы к моменту вызова конструктора базового класса. Если какое-либо из этих свойств используется в логике инициализации базового класса (прямо или косвенно через другую переопределенную open реализацию члена класса), это может привести к некорректному поведению или сбою во время выполнения. Поэтому при разработке базового класса следует избегать использования членов с ключевым словом open в конструкторах, инициализации свойств и блоков инициализации (init).

Вызов функций и свойств суперкласса

Производный класс может вызывать реализацию функций и свойств своего суперкласса, используя ключевое слово super.

open class Rectangle {
    open fun draw() { println("Рисование прямоугольника") }
    val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
    override fun draw() {
        super.draw()
        println("Заполнение прямоугольника")
    }
    val fillColor: String get() = super.borderColor
}

Во внутреннем классе доступ к суперклассу внешнего класса осуществляется при помощи ключевого слова super, за которым следует имя внешнего класса: super@Outer.

class FilledRectangle: Rectangle() {
    override fun draw() {
        val filler = Filler()
        filler. drawAndFill()
    }
    inner class Filler {
        fun fill() { println("Filling") }
        fun drawAndFill() {
            [email protected]() // Вызывает реализацию функции draw() класса Rectangle
            fill()
            println("Нарисованный прямоугольник заполнен ${[email protected]} цветом") // Используется реализация get()-метода свойства borderColor в классе
        }
    }
}

Правила переопределения

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

Для того чтобы отметить конкретный супертип (родительский класс), от которого мы наследуем данную реализацию, используйте ключевое слово super. Для задания имени родительского супертипа используются треугольные скобки, например super<Base>.

open class Rectangle {
    open fun draw() { /* ... */ }
}
interface Polygon {
    fun draw() { /* ... */ } // члены интерфейса открыты ('open') по умолчанию
}
class Square() : Rectangle(), Polygon {
    // Компилятор требует, чтобы функция draw() была переопределена:
    override fun draw() {
        super<Rectangle>.draw() // вызов Rectangle.draw()
        super<Polygon>.draw() // вызов Polygon.draw()
    }
}

Это нормально, наследоваться одновременно от Rectangle и Polygon, но так как у каждого из них есть своя реализация функции draw(), мы должны переопределить draw() в Square и обеспечить нашу собственную реализацию этого метода для устранения получившейся неоднозначности.

Типы наследования: Множественное наследование в C++

Наследование — одна из наиболее важных особенностей объектно-ориентированного программирования (ООП). Способность класса получать свойства и характеристики от другого класса называется наследованием.
Это процесс, в котором пользователи могут создавать новые классы из существующего класса.
Новый класс известен как производный класс, также известный как дочерний класс.
Принимая во внимание, что существующий класс определяется как базовый класс или родительский класс.
Наследование позволяет пользователю повторно использовать код. Во время наследования элементы данных базового класса копируются в производный класс, и к ним можно получить доступ в зависимости от видимости.
Порядок наследования доступности всегда в порядке убывания. От общего к защищенному.

Что такое дочерний и родительский классы?

Дочерний класс: Дочерний класс определяется как класс, который наследует функции другого класса. Также известен как производный класс. Количество дочерних классов, которые могут быть унаследованы от класса с одним родителем, зависит от типа наследования.

Родительский класс: Класс, от которого дочерний класс наследует свои функции и свойства, называется родительским классом. Также известен как базовый класс. Класс с одним родителем может быть производным от нескольких дочерних классов, что может быть типом иерархического наследования. Это также зависит от различных типов наследования.

Синтаксис наследования:

Базовый синтаксис для определения дочернего и родительского классов во всех типах наследования в C++.

 класс parent_class
{
    //определение класса родительского класса
};
класс child_class : режим видимости parent_class
{
   //определение класса дочернего класса
}; 

Синтаксис Описание:

  • Parent_class: Имя родительского класса или базового класса.
  • Child_name: Имя дочернего класса или производного класса.
  • Visibility_mode: режим видимости указывает, как элементы данных дочернего класса наследуются от родительского класса.

Почему мы используем наследование?

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

Режимы наследования:

Существует 3 типа режимов наследования:

  • Открытый режим: В открытом режиме мы можем легко получить доступ ко всем членам базового класса.
  • Частный режим: В частном режиме члены базового класса становятся частными в производном классе.
  • Защищенный режим: В этом защищенном режиме члены базового класса становятся защищенными в производном классе, и теперь члены

Доступен производному классу и его функциям-членам.

Ниже приведена таблица, определяющая контроль производного класса над членами базового класса в различных режимах.

БАЗОВЫЙ КЛАСС ПРОИЗВОДНЫЙ КЛАСС ПРОИЗВОДНЫЙ КЛАСС ПРОИЗВОДНЫЙ КЛАСС
ОБЩЕСТВЕННЫЙ ЗАЩИЩЕННЫЙ ЧАСТНЫЙ
ОБЩЕСТВЕННЫЙ Общественный Защищенный Частный
ЗАЩИТА Защищенный Защищенный Частный
ЧАСТНЫЙ Не унаследовано / остается частным Не унаследовано / остается частным Не унаследовано / остается частным

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

Ниже приведены 5 типов наследования в C++

  • Одиночное наследование
  • Множественное наследование
  • Многоуровневое наследование
  • Иерархическое наследование
  • Гибридное наследование

Одиночное наследование:

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

Синтаксис одиночного наследования в C++:

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

Пример одиночного наследования в C++:

// базовый класс
класс транспортного средства {
    общественность: автомобиль () {
        cout << "Это транспортное средство" << endl;
    }
};
// подкласс, производный от двух базовых классов
класс Автомобиль: общественное транспортное средство { };

// основная функция
интервал основной () {
// создание объекта подкласса вызовет конструктор базовых классов
Объект автомобиля;
вернуть 0; }
 

Результат
Это транспортное средство

Множественное наследование:

В этом типе наследования один производный класс может наследовать от двух или более двух базовых классов. Множественное наследование задает спецификаторы доступа отдельно для всех дочерних классов во время наследования. Конструкторы унаследованных классов вызываются в том же порядке, в котором они наследуются.


Синтаксис множественного наследования в C++

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

Пример множественного наследования в C++:

класс А {
  публичный:
  А() {
       cout << "Вызван конструктор A" << endl;
      }
  };
  класс Б {
    публичный:
    Б () {
         cout << "Вызван конструктор B" << endl;
        }
   };
  class C: public B, public A { // Обратите внимание на порядок
    публичный:
    С () {
         cout << "Вызван конструктор C" << endl;
         }
    };
  интервал основной () {
     С с;
     вернуть 0;
  }
 

Вывод:
Конструктор B вызывается
Конструктор A вызывается
Конструктор C вызывается

Многоуровневое наследование:

В этом типе наследования производный класс создается из другого производного класса. Возьмем пример: есть 3 класса. A — базовый класс, производный от класса B. Следовательно, B — производный класс A. А класс C — производный класс B. Следовательно, B — базовый класс для класса C.

Пример многоуровневого наследования:

класс А {
    публичный:
    А() {
         cout << "Вызван конструктор A" << endl;
        }
    };
    класс B: общественность A {
       публичный:
       Б () {
            cout << "Вызван конструктор B" << endl;
            }
       };
    класс C: общественность B {
      публичный:
      С () {
          cout << "Вызван конструктор C" << endl;
          }
      };
    интервал основной () {
       С с;
       вернуть 0;
   }
 

Вывод:
Конструктор A называется
Конструктор B вызывается
Конструктор C вызывается

Иерархическое наследование:

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

Пример иерархического наследования:

// базовый класс
класс А {
    публичный:
    А() {
       cout<<"Это конструктор A"<< endl;
    }
};
// первый подкласс
класс B: общедоступный {};
// второй подкласс
класс C: общедоступный {};
// основная функция
интервал основной () {
    /* создание объекта подкласса вызовет конструктор базового класса */
  Б объект1;
  С объект2;
 вернуть 0;
}
 

Вывод:
Это конструктор A
Это конструктор B

Гибридное наследование:

Гибридное наследование представляет собой комбинацию двух или более типов наследования. Например, сочетание множественного наследования и многоуровневого наследования может быть объединено вместе, что образует гибридное наследование.

Пример гибридного наследования:

// базовый класс
класс А {
    публичный:
    А() {
         cout << "Это А" << endl;
        }
  };
//базовый класс
класс Б {
    публичный:
    Б () {
         cout<<"Это B " <

Вывод:
Это A
Это B

Наследование в C++ – часто задаваемые вопросы

1.

Есть ли в C множественное наследование?

Нет, как мы знаем, наследование является свойством ООП, т.е. объектно-ориентированного программирования. Поэтому в C нет поддержки наследования на уровне компилятора.

2. В чем разница между множественным и многоуровневым наследованием?

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

При многоуровневом наследовании производный класс создается из другого производного класса. Возьмем пример: есть 3 класса. A является базовым классом, производным от класса B. Следовательно, B является производным классом от A. А класс C является производным от класса B. Таким образом, B является базовым классом для класса C.

3. Что такое суперкласс и подкласс?

Суперкласс определяется как класс, из которого можно создать множество подклассов. Он также известен как родительский класс и базовый класс.
: Подклассы могут наследовать свойства суперклассов. Он наследует свойства суперкласса и создает атрибуты для себя.

4. Каковы преимущества наследования?

Преимущества наследования следующие:
Наследование позволяет повторно использовать код.
Это экономит время, так как нам не нужно переписывать основной код.
Структуру наследования легко понять.
Наследование также улучшает читаемость кода.

5. Каковы особенности наследования?

Основные черты наследования
При наследовании мы можем легко создавать новые классы из существующего класса.
Мы можем повторно использовать элементы данных родительского класса.

6. В чем разница между наследованием и полиморфизмом?

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

Итак, в этом блоге мы попытались объяснить различные типы наследования в C++. Если вы хотите улучшить свои знания любого языка, включая C, C++, Java, Python, вы можете пройти по этой ссылке Prepbyte Courses.

16.1: Наследование — Инженерные тексты LibreText

  1. Последнее обновление
  2. Сохранить как PDF
  • Идентификатор страницы
    29126
    • Патрик МакКланахан
    • Колледж Сан-Хоакин Дельта

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

    Статья разделена на следующие подтемы:

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

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

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

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


    Используя наследование, мы должны написать функции только один раз вместо трех раз, так как мы унаследовали остальные три класса от базового класса (Транспортное средство).

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

    класс имя_дочернего_класса : режим_доступа имя_родительского_класса
    {
      // тело дочернего_класса
    };
     

    Здесь имя_дочернего_класса — имя подкласса,  access_mode — это режим, в котором вы хотите наследовать этот подкласс, например: public, private и т. д., а parent_class_name — — это имя базового класса, от которого вы хотите унаследовать подкласс.

    Примечание . Производный класс не наследует доступ к закрытым элементам данных. Однако он наследует полный родительский объект, который содержит любые закрытые члены, объявленные этим классом.

    // Программа C++ для демонстрации реализации наследования
    #include <бит/stdc++.h>
    использование пространства имен std;
    // Базовый класс
    класс Школа
    {
        публичный:
            строка name_p;
    };
    // Подкласс, наследуемый от базового класса (школы)
    класс Департамент: государственная школа
    {
        публичный:
            строка name_c;
    };
    //основная функция
    основной ()
    {
            отдел отделобж;
            
            // Объект дочернего класса имеет все элементы данных
            // и функции-члены родительского класса
            deptObj.name_c = "Информатика";
            deptObj.name_p = "Колледж Дельта";
            
            cout << "Имя ребенка " << deptObj. name_c << endl;
            cout << "Имя родителя " << deptObj.name_p << endl;
            
            вернуть 0;
    }
     

    В коде есть родительский класс School и дочерний класс Department. У нас могло бы быть гораздо больше переменных и методов в родительском классе, но мы стараемся сделать это простым. Дочерний класс наследует методы и переменные родительского класса - ЗА ИСКЛЮЧЕНИЕМ - дочерний класс не может получить доступ к закрытым переменным или методам родителей.

     

    Вывод:

    Имя ребенка - информатика
    Родительское имя - Колледж Дельта.
     

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

    Адаптировано из:
    «Наследование в C++» Харша Агарвала, Geeks for Geeks распространяется под лицензией CC BY-SA 4.0


    Эта страница под названием 16.1: Наследование распространяется по лицензии CC BY-SA, ее автором, ремиксом и/или куратором является Патрик МакКланахан.

    Оставить комментарий

    Добавить комментарий

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