Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.88/34: Рейтинг темы: голосов - 34, средняя оценка - 4.88
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1

В C++ метод производного класса всегда переопределяет метод базового класса?

30.07.2019, 00:50. Показов 7001. Ответов 22
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#pragma once
 
#include <iostream>
 
using namespace std;
 
class Fish
{
public:
    virtual ~Fish() = default;
 
    Fish(bool isFreshWater) : isFreshWaterFish(isFreshWater) { }
 
    virtual void Swim(bool isFreshWater)
    {
        if (isFreshWater)
            cout << "Swims in lake. "  << endl;
        else
            cout << "Swims in sea" << endl;
    }
 
    virtual void Swim()
    {
        if (isFreshWaterFish)
            cout << "Swims in lake" << endl;
        else
            cout << "Swims in sea" << endl;
    }
 
private:
    bool isFreshWaterFish;
};
 
class Tuna : public Fish
{
public:
    // Раскрытие скрытых текущей реализацией методов Swim базового класса
    using Fish::Swim;
 
    Tuna() : Fish(false) 
    {
        cout << "Tuna swims real fast" << endl;
    }
 
    void Swim() override
    {
        cout << "Tuna swims real fast" << endl;
    }
};
 
class Carp : public Fish
{
public:
    Carp() : Fish(true) { }
 
    // В С++ нет такого понятия, как сокрытие метода базового класса, но и одновременно 
    // и не его переопределение. Если в C# можно скрыть метод, но не переопределить, то в 
    // С++ метод автоматом считается переопределяемым.
    void Swim()
    {
        cout << "Carp swims real slow" << endl;
    }
};
В С++ нет такого понятия, как сокрытие метода базового класса, но и одновременно и не его переопределение. Если в C# можно скрыть метод, но не переопределить, то в С++ метод автоматом считается переопределяемым.
Carp::Swim этот метод хоть и не содержит ключевого слова override, всё равно переопределяет метод базового класса. В C#, например, можно объявить метод не написав override и это не будет считаться переопределением, это будет сокрытием. Если я через ссылку типа базового класса вызову метод Swim, то тот самый метод без override будет проигнорирован и вызовется метод базового класса. Я всё верно понял?

C#
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
 
namespace Inheritance
{
    class Program
    {
        static void Main(string[] args)
        {
            Carp carp = new Carp();
            Tuna tuna = new Tuna();
 
            Fish fish = (Fish)carp;
            fish.Swim();
 
            fish = (Fish)tuna;
            fish.Swim();
 
            carp.Swim();
            tuna.Swim();
        }
    }
 
    public abstract class Fish
    {
        public virtual void Swim()
        {
            Console.WriteLine("Fish.Swim");
        }
    }
 
    public class Carp : Fish
    {
        public override void Swim()
        {
            Console.WriteLine("Carp.Swim");
        }
    }
 
    public class Tuna : Fish
    {
        public new void Swim()
        {
            Console.WriteLine("Tuna.Swim");
        }
    }
}
Миниатюры
В C++ метод производного класса всегда переопределяет метод базового класса?  
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
30.07.2019, 00:50
Ответы с готовыми решениями:

Наследование: почему вызывается метод базового класса, а не производного?
Всем привет, такой вопрос, почему вызывается в данном коде метод базового класса, а не производного (без указания ключевого слова virtual...

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

Вызов метода производного класса через обращение к методу базового класса
Добрый день. Изучаю основы ООП, наткнулся на проблему. Если создавать классы внутри main.cpp, то всё нормально. Если же создавать в...

22
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
30.07.2019, 01:21  [ТС]
Похоже, скрыть можно, если метод базового класса не объявлен как виртуальный.
0
Модератор
 Аватар для Curry
5158 / 3482 / 536
Регистрация: 01.06.2013
Сообщений: 7,549
Записей в блоге: 9
30.07.2019, 01:28
Во первых, есть final.
https://docs.microsoft.com/ru-... dvs.120%29
Во вторых
Цитата Сообщение от Casper-SC Посмотреть сообщение
Если я через ссылку типа базового класса вызову метод Swim, то тот самый метод без override будет проигнорирован и вызовется метод базового класса. Я всё верно понял?
Если в базовом классе метод виртуальный, то вызываться будет метод фактически созданного класса.
override нужен только что бы компилятор мог выругаться если в базовом классе точно такого метода нет.
1
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
30.07.2019, 01:33  [ТС]
Цитата Сообщение от Casper-SC Посмотреть сообщение
Если я через ссылку типа базового класса вызову метод Swim, то тот самый метод без override будет проигнорирован и вызовется метод базового класса.
Это я про C# писал.

Цитата Сообщение от Casper-SC Посмотреть сообщение
Я всё верно понял?
А это про С++ . В общем, редактировал сообщение, получилось непонятно.

Цитата Сообщение от Curry Посмотреть сообщение
Если в базовом классе метод виртуальный, то вызываться будет метод фактически созданного класса.
Понял.
Цитата Сообщение от Casper-SC Посмотреть сообщение
Похоже, скрыть можно, если метод базового класса не объявлен как виртуальный.
Тут я напутал. Если метод базового класса не виртуальный, я создал экземпляр производного класса, а указатель базового класса, то при вызове этого метода вызовется невиртуальный метод базового класса. Только что проверил это. А если метод базового класса виртуальный, то при вызове метода через указатель базового класса вызовется переопределённый метод производного класса. Тоже проверил (собственно, это не противоречит написанному Curry).
1
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
30.07.2019, 02:01
Casper-SC, для не-vitual методов используется просто определение, либо отсутствие определения метода. Если у класса метод не определен, то используется метод ближайшего по иерархии вверх родителя. Кроме того, для любого экземпляра класса всегда можно вызвать метод требуемого родительского класса (это не зависит от виртуальности):

C++
1
2
Carp carp;
carp.Fish::Swim(); // принудительно выполнить метод родительского класса Fish
В случае virtual-метода будет использован наинизший по иерархии метод, вне зависимости от того, какого именно класса был экземпляр или указатель на экземпляр. Важно, каким классом создавался экземпляр. То есть

C++
1
2
3
Carp carp;
Fish &fish = carp;
fish.Swim();  // вызовется Carp::Swim(), несмотря на то, что ссылка имеет тип Fish
При этом в промежуточных и текущем классах отсутствие ключевого слова virtual ни на что не влияет - метод становится виртуальным, если хотя бы в одном из родительских классов он таковым объявлен (ну и соответственно с этого уровня иерархии и будет действовать данное свойство).

override тоже в общем-то ни на что не влияет, и введён только затем, чтобы при случайном удалении слова virtual у какого-то из родительских классов в дочернем возникала ошибка компилятора, показывающая необходимость именно виртуального способа вызова.
2
Модератор
 Аватар для Curry
5158 / 3482 / 536
Регистрация: 01.06.2013
Сообщений: 7,549
Записей в блоге: 9
30.07.2019, 02:03
Цитата Сообщение от TRam_ Посмотреть сообщение
override тоже в общем-то ни на что не влияет, и введён только затем, чтобы при случайном удалении слова virtual
ну, не только. Шире. Что бы при рефакторинге одинаково сигнатуры функций изменить.
2
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
30.07.2019, 02:13
И ещё, по коду выше, насчёт виртуального деструктора. Он наследующим классам не передаётся, потому если нужно удалять объект на основе указателя на родительский класс, т.е. по принципу
C++
1
2
Fish *p_fish = new Carp;
delete p_fish;
то virtual ~Carp() = default; объявлять обязательно, и всем классам выше по иерархии тоже! В противном случае для корректного удаления обязательно при удалении явно преобразовывать тип в тот, которым он создавался.
Наподобие
C++
1
2
Fish *p_fish = new Carp;
delete static_cast<Carp *>(p_fish);
1
285 / 176 / 21
Регистрация: 16.02.2018
Сообщений: 666
30.07.2019, 03:48
Цитата Сообщение от TRam_ Посмотреть сообщение
virtual ~Carp() = default; объявлять обязательно, и всем классам выше по иерархии тоже! В противном случае для корректного удаления обязательно при удалении явно преобразовывать тип в тот, которым он создавался.
Откуда ты это взял?
1
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
30.07.2019, 09:51  [ТС]
Цитата Сообщение от rat0r Посмотреть сообщение
Откуда ты это взял?
Цитата Сообщение от TRam_ Посмотреть сообщение
И ещё, по коду выше, насчёт виртуального деструктора.
Про это я в курсе. Во-первых ReSharper C++ автоматом раздаёт советы по улучшению кода и объясняет кратко в чём проблема. Решарпер мне эту строку в код и добавил. Потом я уже нагуглил:
http://cpp-reference.ru/articl... estructor/
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9006 / 4707 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
31.07.2019, 11:18
Цитата Сообщение от Casper-SC Посмотреть сообщение
В C++ метод производного класса всегда переопределяет метод базового класса?
Casper-SC, слово "всегда" в С++ означает писать книгу. В С++ всегда (не буду так) обычно, есть способ избежать необходимости для отдельно выбранного производного класса или группы таковых, переопределять метод базового класса (кроме деструкторов).
0
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
31.07.2019, 21:24  [ТС]
Цитата Сообщение от IGPIGP Посмотреть сообщение
обычно, есть способ избежать необходимости для отдельно выбранного производного класса или группы таковых, переопределять метод базового класса (кроме деструкторов).
Зачем? Иногда по логике их нужно переопределять. Похоже, мы друг друга не поняли (у меня такое впечатление). То что в С++, не нужно специально переопределять методы в производных классах меня не удивляет. Так и должно быть.

Цитата Сообщение от IGPIGP Посмотреть сообщение
слово "всегда" в С++ означает писать книгу
Хотелось бы услышать более человеческим языком. Я не знаком с выражением "писать книгу" и не понимаю, о чём идёт речь.
0
309 / 221 / 74
Регистрация: 23.05.2011
Сообщений: 981
31.07.2019, 21:53
Цитата Сообщение от TRam_ Посмотреть сообщение
то virtual ~Carp() = default; объявлять обязательно, и всем классам выше по иерархии тоже! В противном случае для корректного удаления обязательно при удалении явно преобразовывать тип в тот, которым он создавался.
Зачем?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
 
class Base{
public:
    virtual ~Base(){std::cout<<"Base\n";}
};
 
struct Checker{
    ~Checker(){std::cout<<"Checker\n";}
};
 
class Inherited: public Base{
    Checker bb;
public:
    ~Inherited(){std::cout<<"Inherited\n";}
};
 
int main()
{
    Base* b = new Inherited();
    delete b;
}
Code
1
2
3
Inherited
Checker
Base
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
31.07.2019, 23:21
Цитата Сообщение от TRam_ Посмотреть сообщение
то virtual ~Carp() = default; объявлять обязательно, и всем классам выше по иерархии тоже! В противном случае для корректного удаления обязательно при удалении явно преобразовывать тип в тот, которым он создавался.
WTF?
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
31.07.2019, 23:30
hoggy, ты уже третий, указывающий на эту мою ошибку. Да, я действительно всегда считал, что виртуальность деструктора не наследуется. Признаю свою ошибку.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9006 / 4707 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
01.08.2019, 00:47
Цитата Сообщение от Casper-SC Посмотреть сообщение
Зачем? Иногда по логике их нужно переопределять.
Кто говорит, что не нужно иногда. Я говорю о вопросе:
Цитата Сообщение от Casper-SC Посмотреть сообщение
В C++ метод производного класса всегда переопределяет метод базового класса?
И утверждаю, что
Цитата Сообщение от IGPIGP Посмотреть сообщение
обычно, есть способ избежать необходимости для отдельно выбранного производного класса или группы таковых, переопределять метод базового класса (кроме деструкторов).
То есть, всегда, не удачное слово, для такого языка.
Цитата Сообщение от Casper-SC Посмотреть сообщение
То что в С++, не нужно специально переопределять методы в производных классах меня не удивляет.
Что значит в С++ не нужно? С++ это не лес, который определяет нужность или не нужность в его чаще, что-то определять. Нужно ли переопределять решает программист. При чём тут С++? Casper-SC, если бы вы спросили о том, как переопределяются виртуальные методы или что-то ещё, в менее общих, но тем не менее категоричных формулировках, легче было бы вас понять.
Но если кратко, то мой ответ на вопрос топика:
Цитата Сообщение от Casper-SC Посмотреть сообщение
В C++ метод производного класса всегда переопределяет метод базового класса?
Нет.
Могут быть методы которых вообще нет в базовом классе.
По примеру, - метод совпадающий по сигнатуре с виртуальным, - всегда переопределение, да.
override - подсказка компилятору о том, что вы намереваетесь этим методом перегрузить метод в базовом классе. Это спасёт, например, если вы ошибётесь в сигнатуре и не заметите этого. Беда будет (без override) не только в том, что вы не получите переопределения. Вы потеряете сам оригинал виртуального метода в наследнике.
1
Модератор
 Аватар для Curry
5158 / 3482 / 536
Регистрация: 01.06.2013
Сообщений: 7,549
Записей в блоге: 9
01.08.2019, 01:04
Цитата Сообщение от IGPIGP Посмотреть сообщение
Это спасёт, например, если вы ошибётесь в сигнатуре и не заметите этого. Беда будет (без override) не только в том, что вы не получите переопределения. Вы потеряете сам оригинал виртуального метода в наследнике.
Почему он потеряется? Он будет доступен, у него же другая сигнатура.
Хуже, если метод повторить в наследнике не предполагая что у базового уже есть такой виртуальный. Такое возможно, особенно, с короткими именами и простыми сигнатурами.
Интересно, есть ли в каких ни будь компиляторах флаг включающий предупреждение для таких случаев?
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
01.08.2019, 01:35
Цитата Сообщение от Curry Посмотреть сообщение
Хуже, если метод повторить в наследнике не предполагая что у базового уже есть такой виртуальный.
Я хоть и новичек, но мне трудно представить, почему это может стать проблемой. Если вы переопределили виртуальный метод базового, то вызывая его из потомка вы его и вызовите. А если вы вызываете его по ссылке на базовый класс(не предполагая что он виртуальный), то тут что-то не то(с программистом), но и в этом случае вызовется именно он.
1
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9006 / 4707 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
01.08.2019, 09:25
Цитата Сообщение от IGPIGP Посмотреть сообщение
вы намереваетесь этим методом перегрузить метод в базовом классе
переопределить! "перегрузить" - тут совершенно неуместно. Интересно читать утром то, что ночью пишешь.

Добавлено через 18 минут
Цитата Сообщение от Curry Посмотреть сообщение
Почему он потеряется? Он будет доступен, у него же другая сигнатура.
Он будет доступен лишь через указатель базового класса. А через ссылку на наследника он исчезнет сразу как только появится "братец" с иной сигнатурой. Для того чтобы он снова появился, придётся написать перегрузку с точно той что нужно сигнатурой явно. Когда нет такого "братца" не нужно ни чего явно писать. И так он у вас есть (не переопределённый правда)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
 
using namespace std;
struct Base
{
    virtual void foo(){cout<<"\nbase\n";}
    virtual void nobro(){cout<<"\nnobro\n";}
 
};
 
struct ChildDefault : public Base
{
    void foo(int){cout<<"\nchild non-virtual\n";}
 
 
};
 
int main()
{
ChildDefault chd;
Base * bs(&chd);
bs->foo();
//chd.foo();//нету -раскомментируйте и получите ошибку
chd.nobro();//тут нормально
    return 0;
}
2
Эксперт .NET
 Аватар для Casper-SC
4434 / 2094 / 404
Регистрация: 27.03.2010
Сообщений: 5,657
Записей в блоге: 1
01.08.2019, 20:07  [ТС]
Цитата Сообщение от IGPIGP Посмотреть сообщение
Для того чтобы он снова появился, придётся написать перегрузку с точно той что нужно сигнатурой явно.
Можно написать в производном классе using Base::foo; и ничего не придётся переопределять явно.

Работает:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
 
using namespace std;
 
struct Base
{
    virtual void foo() { cout << "\nbase\n"; }
    virtual void nobro() { cout << "\nnobro\n"; }
};
 
struct ChildDefault : public Base
{
    using Base::foo;
 
    void foo(int) { cout << "\nchild non-virtual\n"; }
};
 
int main()
{
    ChildDefault chd;
    Base* bs(&chd);
    bs->foo();
    chd.foo();     // Заработало благодаря using Base::foo;
    chd.nobro();
    return 0;
}
1
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9006 / 4707 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
01.08.2019, 20:20
Цитата Сообщение от Casper-SC Посмотреть сообщение
Можно написать в производном классе using Base::foo; и ничего не придётся переопределять явно.
Можно всё сделать через место, которым не нужно. Есть вообще, возможность определить реализацию по умолчанию, для чисто виртуального метода, например. Только к месту ли? Я же говорил о ключевом слове override. Ведь если кто-то нечаянно изменит сигнатуру или вы сами ошибётесь, то все методы с верной сигнатурой вниз по иерархии будут отрезаны. И чувствовать будут себя отлично. Все будет работать. Не везде как хотелось бы. О перегрузке же был вопрос?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
01.08.2019, 20:20
Помогаю со студенческими работами здесь

Как сложить объект базового класса с объектом производного(наследуемого класса)
Как умножить объект базового класса с объектом производного(наследуемого класса): ozenka - объект базового класса, а ves- производного ...

Создание указателя типа базового класса на экземпляр производного класса
Добрый день! Иногда видел коды, где создавался указатель типа базового класса на объект класса - наследника, для чего это может применяться?

Указатель на объект базового класса и адрес объекта производного класса
Пример кода: class Class1 { public: Class1(int x) { j = new int; *j = x; } ~Class1() {delete j;}

Вызвать конструктор производного класса без конструктора базового класса
Здравствуйте! У меня есть базовый класс треугольник и производный класс равносторонний треугольник.В конструкторе треугольника вводятся...

Возможно ли указатель производного класса инициализировать объектом базового класса?
имеется связка наследуемых классов A-&gt;B а от B наследуются одновременно еще два класса B-&gt;C и B-&gt;D class A { public: ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Символьное дифференцирование
igorrr37 13.02.2026
/ * Программа принимает математическое выражение в виде строки и выдаёт его производную в виде строки и вычисляет значение производной при заданном х Логарифм записывается как: (x-2)log(x^2+2) -. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru