Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.73/15: Рейтинг темы: голосов - 15, средняя оценка - 4.73
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 231

Виртуальность friend-метода в базовом классе

20.02.2019, 10:02. Показов 3129. Ответов 20
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Привет всем. Прошу посмотрите данный код.
В производном классе реализована перегрузка оператора <<.
Вопрос как реализовать виртуальность этого friend-метода в базовом классе? Что бы код ниже мог работать.

C++ (Qt)
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
class ICommand
{
public:
    virtual void Execute() = 0;
};
 
class LightsCommand : public ICommand
{
public:
    void Execute() { cout << "Light on" << endl; }
 
    friend ostream& operator<< (ostream &out, const LightsCommand &lc);
};
 
ostream& operator<< (ostream &out, const LightsCommand &lc)
{
    out << "Turn on the light";
    return out;
}
 
int main()
{
    map<string, shared_ptr<ICommand>> _commands;
 
    _commands["1"] = shared_ptr<ICommand>(new LightsCommand());
    _commands["2"] = shared_ptr<ICommand>(new LightsCommand());
 
    for (auto it = _commands.begin(); it != _commands.end(); ++it)
    {
        cout << it->first << " \t - ";
        cout << *(it->second);
        cout << endl;
    }
 
    return 0;
}
Иначе при вызове:
cout << *(it->second);
возникает ошибка.
1
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.02.2019, 10:02
Ответы с готовыми решениями:

Указатель на потомка в базовом классе.
Мне нужно создать в базовом классе массив указателей на потомки этого класса. Помогите - как это сделать?

Описание функции в базовом классе
Всем привет! Есть базовый класс TBaseForm, в заголовочном файле описана функция: public: // User declarations void...

Инициализация статической переменной в базовом классе
Есть класс Tank. От этого класса будут наследники MyTank и EnemyTank. В классе Tank есть статическая переменная _hWnd, она общая для всех...

20
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
20.02.2019, 10:15
Пометил исправленные строки.

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
#include <iostream>
#include <map>
#include <memory>
using namespace std;
 
class ICommand
{
public:
    virtual void Execute() = 0;
};
 
class LightsCommand : public ICommand
{
public:
    void Execute() { cout << "Light on" << endl; }
};
 
ostream& operator<< (ostream &out, const ICommand &lc) //1
{
    out << "Turn on the light";
    return out;
}
 
int main()
{
    map<string, shared_ptr<ICommand>> _commands;
 
    _commands["1"] = shared_ptr<ICommand>(new LightsCommand());
    _commands["2"] = shared_ptr<ICommand>(new LightsCommand());
 
    for (auto it = _commands.begin(); it != _commands.end(); ++it)
    {
        cout << it->first << " \t - ";
        cout << *(it->second);
        cout << endl;
    }
 
    return 0;
}
Добавлено через 5 минут
Ты в map хранишь ICommand, следовательно оператор должен принимать в качестве параметра ICommand. Но также ты можешь !!передать!! LightsCommand в качестве параметра.
Такой код тоже сработает
C++
1
2
3
4
5
6
7
8
9
10
11
    map<string, shared_ptr<LightsCommand>> _commands;
 
    _commands["5"] = shared_ptr<LightsCommand>(new LightsCommand());
    _commands["9"] = shared_ptr<LightsCommand>(new LightsCommand());
 
    for (auto it = _commands.begin(); it != _commands.end(); ++it)
    {
        cout << it->first << " \t - ";
        cout << *(it->second);
        cout << endl;
    }
Советую разобраться как устроенно наследование в c++
0
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
20.02.2019, 10:17
Цитата Сообщение от oleggy Посмотреть сообщение
Вопрос как реализовать виртуальность этого friend-метода в базовом классе? Что бы код ниже мог работать.
по ходу никак, но можно по другому
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
#include <iostream>
#include <ostream>
#include <map>
 
using namespace std;
 
class ICommand
{
public:
    virtual void Execute() = 0;
    virtual ostream& operator<< (ostream&) = 0;
};
 
class LightsCommand : public ICommand
{
public:
    void Execute() { cout << "Light on" << endl; }
 
    virtual ostream& operator<< (ostream &out);
};
 
ostream& LightsCommand::operator<< (ostream &out)
{
    out << "Turn on the light";
    return out;
}
 
int main()
{
    ICommand* p = new LightsCommand{};
    p->operator<<(cout);
 
    return 0;
}
0
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 231
20.02.2019, 10:22  [ТС]
DARKPALADIN, не совсем так.
Не с проста я использую в map указатель на базовый класс. Я хочу использовать позднее связывание. Допустим добавить другой производный класс.
Расширил код.

C++ (Qt)
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
class ICommand
{
public:
    virtual void Execute() = 0;
};
 
class LightsCommand : public ICommand
{
public:
    void Execute()
    {
        cout << "Light on" << endl;
    }
 
    friend ostream& operator<< (ostream &out, const LightsCommand &lc);
};
 
ostream& operator<< (ostream &out, const LightsCommand &lc)
{
    out << "Turn on the light";
    return out;
}
 
class TvCommand : public ICommand
{
public:
    void Execute()
    {
        cout << "Switch on TV" << endl;
    }
 
    friend ostream& operator<< (ostream &out, const TvCommand &tc);
};
 
ostream& operator<< (ostream &out, const TvCommand &tc)
{
    out << "Turn on the TV";
    return out;
}
 
int main()
{
    map<string, shared_ptr<ICommand>> _commands;
 
    _commands["1"] = shared_ptr<ICommand>(new LightsCommand());
    _commands["2"] = shared_ptr<ICommand>(new TvCommand());
 
    for (auto it = _commands.begin(); it != _commands.end(); ++it)
    {
        cout << it->first << " \t - ";
        cout << *(it->second);
        cout << endl;
    }
 
    return 0;
}
Как сделать так что бы оператор << для каждого класса вызывался свой через указатель базового класса?
Соответственно в базовом классе надо определить виртуальную дружественную функцию. Но как?
0
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
20.02.2019, 10:24
oleggy, у тебя оператор << реализован вне класса, позднее связывание для функций реализованных вне класса не получится сделать.
0
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 231
20.02.2019, 10:25  [ТС]
_stanislav,
А как тогда сделать так что бы оператор << для каждого класса вызывался собственный, через указатель базового класса?
Соответственно в базовом классе надо определить виртуальную дружественную функцию. Но как?
0
19499 / 10104 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
20.02.2019, 10:28
Цитата Сообщение от oleggy Посмотреть сообщение
виртуальную дружественную функцию
Дружественность не наследуется.

Цитата Сообщение от oleggy Посмотреть сообщение
как?
Сделать виртуальный метод, который вызывать из перегруженного оператора.
1
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
20.02.2019, 10:28
только так https://www.cyberforum.ru/post13341693.html
1
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 231
20.02.2019, 10:30  [ТС]
Вы могли бы ткнуть носом меня и написать небольшой код, как бы это выглядело в этом примере?
0
19499 / 10104 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
20.02.2019, 10:32
Лучший ответ Сообщение было отмечено oleggy как решение

Решение

На основе примера _stanislav:
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
#include <iostream>
#include <ostream>
#include <map>
 
using namespace std;
 
class ICommand
{
public:
    virtual void Execute() = 0;
    virtual ostream& stream_out(ostream&) const = 0;
};
 
class LightsCommand : public ICommand
{
public:
    void Execute() { cout << "Light on" << endl; }
 
    virtual ostream& stream_out(ostream &out) const;
};
 
ostream& LightsCommand::stream_out(ostream &out) const
{
    out << "Turn on the light";
    return out;
}
 
ostream & operator<<(ostream & out, ICommand const & ic)
{
    return ic.stream_out(out);
}
 
int main()
{
    ICommand* p = new LightsCommand{};
    cout << *p;
 
    return 0;
}
2
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 231
20.02.2019, 10:38  [ТС]
Я правильно понял что решение _stanislav не использует дружественность что бы можно было реализовать виртуальность оператора << но в "неудобоваримом" представлении - ostream& operator<< (ostream &out).

А вариант DrOffset, расширяет этот пример. Искусственно переоборудовав метод "ostream& operator<< (ostream &out)." в классический вариант "cout << ..." ?
0
19499 / 10104 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
20.02.2019, 10:43
oleggy, по существу виртуальный метод нет нужды называть operator<< именно из-за его "неудобоваримости".
А то, что я показал - это практически идиома virtual friend (friend здесь можно добавить, если спрятать stream_out в protected)

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
class ICommand
{
public:
    virtual void Execute() = 0;
 
protected:
    virtual ostream& stream_out(ostream&) const = 0;
 
    friend ostream & operator<<(ostream & out, ICommand const & ic)
    {
        return ic.stream_out(out);
    }
};
0
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
20.02.2019, 10:45
Цитата Сообщение от DrOffset Посмотреть сообщение
На основе примера _stanislav:
своеобразный адаптер, у Страуструпа в книге вроде что то подобное было?
The C++ Programming Language Special Edition 2011
0
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
20.02.2019, 10:50
Цитата Сообщение от oleggy Посмотреть сообщение
Не с проста я использую в map указатель на базовый класс. Я хочу использовать позднее связывание. Допустим добавить другой производный класс.
Используй Нельзя создать виртуальный оператор ostream, но можно сделать виртуальную функцию которая будет вызываться при вызове оператора

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
#include <iostream>
#include <map>
#include <memory>
using namespace std;
 
class ICommand
{
public:
    virtual void Execute() = 0;
    
    virtual void print(ostream& os) const = 0;
};
 
class LightsCommand : public ICommand
{
public:
    void Execute()
    {
        cout << "Light on" << endl;
    }
    
    void print(ostream& os) const {
        os << "Turn on the light";
    }
 
};
 
class TvCommand : public ICommand
{
public:
    void Execute()
    {
        cout << "Switch on TV" << endl;
    }
    
    void print(ostream& os) const {
        os << "Turn on the TV";
    }
 
};
 
ostream& operator<< (ostream &out, const ICommand &obj)
{
    obj.print(out);
    return out;
}
 
int main()
{
    map<string, shared_ptr<ICommand>> _commands;
 
    _commands["1"] = shared_ptr<ICommand>(new LightsCommand());
    _commands["2"] = shared_ptr<ICommand>(new TvCommand());
 
    for (auto it = _commands.begin(); it != _commands.end(); ++it)
    {
        cout << it->first << " \t - ";
        cout << *(it->second);
        cout << endl;
    }
 
    return 0;
}
Добавлено через 2 минуты
oleggy, "DrOffset" - предложил правильное и разумное решение.
1
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 231
20.02.2019, 10:53  [ТС]
DARKPALADIN, ваш пример по своему прост даже.
Вы выходит "маскируете" метод print переопределенным методом "operator<<" что бы можно в коде использовать "<<" так?
А чем ваше решение хуже чем вышеописанные?
0
20.02.2019, 10:54

Не по теме:

Цитата Сообщение от _stanislav Посмотреть сообщение
у Страуструпа в книге вроде что то подобное было?
Честно говоря, я не помню в какой книге об этом было.

0
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
20.02.2019, 10:54
Цитата Сообщение от oleggy Посмотреть сообщение
о в "неудобоваримом" представлении
в удобноваримом представлении operator << вызывается статически, через таблицу функций не вызвать.
0
19499 / 10104 / 2461
Регистрация: 30.01.2014
Сообщений: 17,816
20.02.2019, 10:54
oleggy, мне кажется оно точно такое же
1
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
20.02.2019, 11:01
Цитата Сообщение от oleggy Посмотреть сообщение
Вы выходит "маскируете" метод print переопределенным методом "operator<<" что бы можно в коде использовать "<<" так?
Почти. Что бы "замаскировать (можно только переопределять, но не использовать из вне)" метод print нужно в базовом классе:
C++
1
2
3
4
protected: 
    virtual void print(ostream& os) const = 0;
    
    friend ostream& operator<< (ostream &out, const ICommand &obj);
В остальных классах (наследниках) просто метод print помечать как protected:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LightsCommand : public ICommand
{
public:
    void Execute()
    {
        cout << "Light on" << endl;
    }
 
protected:    
    void print(ostream& os) const {
        os << "Turn on the light";
    }
 
};
Добавлено через 1 минуту
oleggy, И я не переопределяю operator<<, оператор просто знает, у какого класса вызвать функцию print. Все это связано с наследованием и таблицей виртуальных функций

Добавлено через 15 секунд
oleggy, И я не переопределяю operator<<, оператор просто знает, у какого класса вызвать функцию print. Все это связано с наследованием и таблицей виртуальных функций

Добавлено через 13 секунд
oleggy, И я не переопределяю operator<<, оператор просто знает, у какого класса вызвать функцию print. Все это связано с наследованием и таблицей виртуальных функций
0
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
20.02.2019, 11:25
Цитата Сообщение от DrOffset Посмотреть сообщение
Честно говоря, я не помню в какой книге об этом было.
21.2.3.1. Виртуальные функции вывода
Функции-члены класса ostream не виртуальные. Операции вывода, добавляемые
пользователем, не являются функциями-членами, и, значит, они тоже не
виртуальные. Одна из причин такого положения дел состоит в желании достичь
максимальной эффективности для операции помещения символа в буфер. Это то место, где
критически важна производительность, и поэтому нужно обеспечить встраивание
функций. А виртуальные функции применяются для гибкости операций, имеющих
дело с переполнением (и переопустошением) буфера (§21.6.4).
Тем не менее, иногда программисту нужно вывести объект, для которого
известен лишь базовый класс. Поскольку точный тип неизвестен, корректность вывода
недостижима путем определения операции « для всех новых типов. Вместо этого
в абстрактном базовом классе можно определить виртуальную функцию вывода:

C++
1
2
3
4
5
6
7
8
9
class My_base 
{ 
public: 
    virtual ostream& put {ostream& s) const = 0; //пишет *this в s 
}; 
ostream& operator<<{ostream& s, const Mybase& r) 
{ 
return r.put{s) ; //используется правильная put() 
}
Здесь функция put() является виртуальной функцией, обеспечивающей
корректность вывода при использовании операции <<.
Теперь мы можем написать:

C++
1
2
3
4
5
6
7
8
9
class Sometype: public My base 
{ 
public: 
ostream & put (ostream & s) const; // замещает My_base: :put() 
}; 
void f(const My base& r, Sometype& s) // используется «, вызывающая правильную риф 
{ 
cout << r << s; 
}
Таким образом виртуальная функция put () интегрируется в программный
каркас, формируемый классом ostream и операциями <<. Это полезный универсальный
прием для введения операций, которые ведут себя как виртуальные функции с
выбором на этапе выполнения на основе их второго аргумента.


Bjarne Stroustrup
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
20.02.2019, 11:25
Помогаю со студенческими работами здесь

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

Создание ссылки на производный класс в базовом классе
Доброго времени суток. Есть такой вопрос: &quot;Как обратиться к методам производного класса из базового?&quot; Если создать ссылку на...

Вызов события в производном классе, которое объявлено в базовом
Доброго времени суток, Господа. Возник вопрос. Есть класс A: public abstract class A { public event...

Как обработать исключение из класса наследника в базовом классе
Добрый день. Пишу приложение Windows Forms на C#. У меня есть базовый класс, у которого есть: одно общее свойство, конструктор базового...

Как работают static-переменные объявленные в в базовом классе?
Информации увидел много, но суть так и не могу уловить. Образовалась пара вопросов: 1. Если я создаю static-переменную в классе и делаю...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Камера 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 с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru