Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 223
1

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

20.02.2019, 10:02. Показов 1976. Ответов 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
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
20.02.2019, 10:02
Ответы с готовыми решениями:

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

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

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

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

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

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
Нарушитель
1490 / 1291 / 486
Регистрация: 16.08.2014
Сообщений: 5,419
Записей в блоге: 1
20.02.2019, 10:17 3
Цитата Сообщение от 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
Сообщений: 223
20.02.2019, 10:22  [ТС] 4
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
Нарушитель
1490 / 1291 / 486
Регистрация: 16.08.2014
Сообщений: 5,419
Записей в блоге: 1
20.02.2019, 10:24 5
oleggy, у тебя оператор << реализован вне класса, позднее связывание для функций реализованных вне класса не получится сделать.
0
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 223
20.02.2019, 10:25  [ТС] 6
_stanislav,
А как тогда сделать так что бы оператор << для каждого класса вызывался собственный, через указатель базового класса?
Соответственно в базовом классе надо определить виртуальную дружественную функцию. Но как?
0
15134 / 8129 / 1964
Регистрация: 30.01.2014
Сообщений: 13,816
20.02.2019, 10:28 7
Цитата Сообщение от oleggy Посмотреть сообщение
виртуальную дружественную функцию
Дружественность не наследуется.

Цитата Сообщение от oleggy Посмотреть сообщение
как?
Сделать виртуальный метод, который вызывать из перегруженного оператора.
1
Нарушитель
1490 / 1291 / 486
Регистрация: 16.08.2014
Сообщений: 5,419
Записей в блоге: 1
20.02.2019, 10:28 8
только так https://www.cyberforum.ru/post13341693.html
1
7 / 7 / 0
Регистрация: 14.03.2013
Сообщений: 223
20.02.2019, 10:30  [ТС] 9
Вы могли бы ткнуть носом меня и написать небольшой код, как бы это выглядело в этом примере?
0
15134 / 8129 / 1964
Регистрация: 30.01.2014
Сообщений: 13,816
20.02.2019, 10:32 10
Лучший ответ Сообщение было отмечено 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
Сообщений: 223
20.02.2019, 10:38  [ТС] 11
Я правильно понял что решение _stanislav не использует дружественность что бы можно было реализовать виртуальность оператора << но в "неудобоваримом" представлении - ostream& operator<< (ostream &out).

А вариант DrOffset, расширяет этот пример. Искусственно переоборудовав метод "ostream& operator<< (ostream &out)." в классический вариант "cout << ..." ?
0
15134 / 8129 / 1964
Регистрация: 30.01.2014
Сообщений: 13,816
20.02.2019, 10:43 12
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
Нарушитель
1490 / 1291 / 486
Регистрация: 16.08.2014
Сообщений: 5,419
Записей в блоге: 1
20.02.2019, 10:45 13
Цитата Сообщение от DrOffset Посмотреть сообщение
На основе примера _stanislav:
своеобразный адаптер, у Страуструпа в книге вроде что то подобное было?
The C++ Programming Language Special Edition 2011
0
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
20.02.2019, 10:50 14
Цитата Сообщение от 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
Сообщений: 223
20.02.2019, 10:53  [ТС] 15
DARKPALADIN, ваш пример по своему прост даже.
Вы выходит "маскируете" метод print переопределенным методом "operator<<" что бы можно в коде использовать "<<" так?
А чем ваше решение хуже чем вышеописанные?
0
DrOffset
20.02.2019, 10:54
  #16

Не по теме:

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

0
Нарушитель
1490 / 1291 / 486
Регистрация: 16.08.2014
Сообщений: 5,419
Записей в блоге: 1
20.02.2019, 10:54 17
Цитата Сообщение от oleggy Посмотреть сообщение
о в "неудобоваримом" представлении
в удобноваримом представлении operator << вызывается статически, через таблицу функций не вызвать.
0
15134 / 8129 / 1964
Регистрация: 30.01.2014
Сообщений: 13,816
20.02.2019, 10:54 18
oleggy, мне кажется оно точно такое же
1
7 / 7 / 8
Регистрация: 19.06.2016
Сообщений: 134
20.02.2019, 11:01 19
Цитата Сообщение от 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
Нарушитель
1490 / 1291 / 486
Регистрация: 16.08.2014
Сообщений: 5,419
Записей в блоге: 1
20.02.2019, 11:25 20
Цитата Сообщение от 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
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
20.02.2019, 11:25

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.