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

Неочевидные грабли полиморфизма с++

23.11.2011, 01:05. Показов 17165. Ответов 101
Метки нет (Все метки)

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


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
#include <iostream>
using namespace std;
 
struct A
{
    virtual void Func1(){cout<<"A::Func1"<<endl;}
};
struct B
{ 
    virtual void Func2(){cout<<"B::Func2"<<endl;}}
;
struct C: public A, public B
{ 
    virtual void Func3(){cout<<"C::Func3"<<endl;}
};
 
int main()
{
    A *ptr = new C;
    void *ptr1 = new C;
    
    ((A*)ptr)->Func1(); //вывод: A::Func1
    ((B*)ptr)->Func2(); //вывод: A::Func1 Почему?
    ((C*)ptr)->Func3(); //вывод: C::Func3
    
    A* aPtr=dynamic_cast<A*>(ptr);
    B* bPtr=dynamic_cast<B*>(ptr);
    C* cPtr=dynamic_cast<C*>(ptr)
    
    //нельзя кастовать от void*
    //void* ничего не знает ни о каких классах
    //и ни о каких таблицах виртуальных функций
 
    C* cPtr=dynamic_cast<C*>(ptr1); //ошибка компиляции
    //error C2681: void *: недопустимый тип выражения для dynamic_cast
    
    aPtr->Func1(); //вывод: A::Func1
    bPtr->Func2(); //вывод: B::Func2
    cPtr->Func3(); //вывод: C::Func3
 
    delete ptr;
    delete ptr1;
 
    return 0;
}
От себя могу добавить, что разные компиляторы по разному могут реализовывать механизм полиморфизма. Поэтому, при множественном наследовании если что и спасет - только dynamic_cast
Но в любом случае, нужно быть предельно осторожным.

А по сути, полиморфизм + множественное наследование = мина замедленного действия.

Поэтому, при проектировании полиморфных классов, лучше до последнего избегать множественного наследования.
1
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
23.11.2011, 01:05
Ответы с готовыми решениями:

Неочевидные особенности выдачи pg таблиц
Я довольно много копаюсь в pg_* таблицах, дабы получать информацию о базе данных и прочих системных фишечках. Ближе к делу: ...

Неочевидные результаты очевидных css-свойств
Добрый вечер, господа верстальщики! Столкнулся с такой неочевидной для меня проблемой: Оказывается, добавив свойство padding:1px,...

Грабли с кодировкой
Здравствуйте! В конструкторе главного окна: MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new...

101
Заблокирован
24.11.2011, 11:34  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Deviaphan Посмотреть сообщение
Почему ты думаешь, что строки 20-21 вообще есть в коде?
Глуповатый вопрос: почему строки написанного кода присутствуют в коде?

Цитата Сообщение от Deviaphan Посмотреть сообщение
Приведение типов static_cast - создание копии.

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
struct Base
{
    Base(): a(333){}
    Base( const Base& istok) { std::cout << "base\n"; }
    int a;
};
 
 
struct Derrived:public Base
{
    Derrived():Base() {}
    Derrived( const Derrived& istok): Base(istok) { std::cout << "Derrived\n"; }
};
 
 
int main()
{
    
    Derrived t;
    static_cast<Base>(t); //по мнению господина Deviaphan
                            //здесь должен запустится копирующий конструктор
                        //как может объяснить господин тот факт,
                   // что никакого конструктора не запускается?
    return 0;
};
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:42
Придумал поясняющий пример!
C++
1
2
3
4
5
6
7
8
9
10
int a = 123;
double b = 3.1415;
 
int c = (int)b; // приведение типа
//int c = static_cast<int>(b);
double d = (double)a; // тоже приведение
//double c = static_cast<double>(a);
 
int e = *((int*)&b); // интерпретация указателя на double, как указателя на int. Или приведение типа указателя. Присутствует "срезка".
// int e = *reinterpret_cast<int*>(&b);
Добавлено через 4 минуты
Цитата Сообщение от Bers Посмотреть сообщение
//по мнению господина Deviaphan //здесь должен запустится копирующий конструктор //как может объяснить господин тот факт, // что никакого конструктора не запускается?
Запускается. Специально для тебя проверил. В консоли написано "base".

Добавлено через 1 минуту
Можешь убедиться
http://codepad.org/wOdn0CEx
0
Заблокирован
24.11.2011, 11:45
Цитата Сообщение от Bers Посмотреть сообщение
Код
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Base
{
    Base(): a(333){}
    Base( const Base& istok) { std::cout << "base\n"; }
    int a;
};
 
struct Derrived:public Base
{
    Derrived():Base() {}
    Derrived( const Derrived& istok): Base(istok) { std::cout << "Derrived\n"; }
};
 
int main()
{
    
    Derrived t;
    (Base)t; //по мнению Deviaphan  здесь
                 //должен запустится копирующий 
                 //конструктор
     
    return 0;
}


Почему же тогда не запустился копирующий конструктор, а??
Как вы можете это объяснить?
Вы заблуждаетесь. В вашем примере в выражении (Base)t; создается неименованный временный объект посредством вызова конструктора копирования

C++
1
    Base( const Base& istok) { std::cout << "base\n"; }
Что это означает? Это означает, что в стеке выделяется новый участок памяти для этого объекта. Исходный объект остался в своей памяти совершенно без изменения. То есть теперь у вас два объекта. Правда время жизни нового объекта заканчивается в том же предложении, где он был и сооздан. Но вы можете создать константную ссылку на него и тогда объект будет продолжать жить пока ссылка не выйдет из области своей видимости:

C++
1
const Base &rb = (Base)t;
То есть никакой речи об "интерпретации" памяти здесь не идет. Здесь имеет место быть два отдельных объекта.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:49
Цитата Сообщение от Bers Посмотреть сообщение
Глуповатый вопрос: почему строки написанного кода присутствуют в коде?
А ты глянь на ассемблерный листинг. Нет ни создания объекта t, ни привидения типа. Есть только код помещения текста в консоль.)

Добавлено через 58 секунд
Цитата Сообщение от Сыроежка Посмотреть сообщение
То есть никакой речи об "интерпретации" памяти здесь не идет. Здесь имеет место быть два отдельных объекта.
Причём ссылка происходит на временный объект, которого в следующей строке уже нет.

Добавлено через 1 минуту
Цитата Сообщение от Сыроежка Посмотреть сообщение
Но вы можете создать константную ссылку на него и тогда объект будет продолжать жить пока ссылка не выйдет из области своей видимости
Объекта уже не будет. Ссылка битая. Равнозначна указателю на мусор.

Добавлено через 1 минуту
Цитата Сообщение от Сыроежка Посмотреть сообщение
Но вы можете создать константную ссылку на него
Это работает для аргументов функций, т.к. временный объект разрушается только после возврата из функции.
0
Заблокирован
24.11.2011, 11:50  [ТС]
Все, я кажется понял.

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
struct Base
{
    Base(): a(333){}
    Base( const Base& istok) { std::cout << "Copy base\n"; }
    void View(){ std::cout << "base\n"; }
    int a;
};
 
 
struct Derrived:public Base
{
    Derrived():Base() {}
    Derrived( const Derrived& istok): Base(istok) { std::cout << "Copy Derrived\n"; }
    void View(){ std::cout << "Derrived\n"; }
};
 
 
int main()
{
    Derrived t;
    t.View(); //вывод: Derrived
 
    std::cout << "*******\n";
 
    (static_cast<Base>(t)).View(); //вывод: Copy base а потом base
 
    //((Base) t).View(); //вывод: Copy base а потом base
    return 0;
};
В предыдущих примерах-иллюстрациях копирующий конструктор не запускался, потому что не было нужны в том, что бы использовать приведенный объект по назначению.

А так, на самом деле приведение одного типа к другому - действительно запускает копирующий конструктор. Господин Deviaphan совершенно прав.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:54
Ура-ура, можно перестать биться головой об стенку.)

Теперь попробуй в этом примере заменить static_cast на reinterpret_cast и убедись, что интерпретация не скалярного типа невозможна.
0
Заблокирован
24.11.2011, 11:54
Цитата Сообщение от Deviaphan Посмотреть сообщение
Добавлено через 1 минуту

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

И сам объект и ссылка на него будут жить до тех пор пока ссылка находится в области своей видимости. Так как она объявлена в теле main, то и будет жить в теле этой функции, пока та не завершит свою работу.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:57
Цитата Сообщение от Bers Посмотреть сообщение
В предыдущих примерах-иллюстрациях копирующий конструктор не запускался, потому что
Не корректный компилятор или настройки. codepad и MSVC (причём в релизе!) копирующий конструктор всё равно запускали. Вывод в консоль это тоже код, который нельзя игнорировать.
Пользуйтесь правильными компиляторами.

Добавлено через 1 минуту
Цитата Сообщение от Сыроежка Посмотреть сообщение
то и будет жить в теле этой функции
Ссылка - да. Но временный объект уже будет разрушен. Можешь проверить.

Добавлено через 38 секунд
Цитата Сообщение от Сыроежка Посмотреть сообщение
И сам объект и ссылка на него будут жить до тех пор пока ссылка находится в области своей видимости.
Интеллектуальных ссылок не бывает.
0
Заблокирован
24.11.2011, 12:01
Цитата Сообщение от Deviaphan Посмотреть сообщение
Не корректный компилятор или настройки. codepad и MSVC (причём в релизе!) копирующий конструктор всё равно запускали. Вывод в консоль это тоже код, который нельзя игнорировать.
Пользуйтесь правильными компиляторами.

Добавлено через 1 минуту

Ссылка - да. Но временный объект уже будет разрушен. Можешь проверить.

Добавлено через 38 секунд

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

Вам еще рано меня поправлять. Вам следует сначала глубже изучить С++! Несете всякую чушь, да еще с таким пафосом.

Надеюсь, третий раз вы эту чушь нести не будете. Если все же есть зуд, то сначала снова перечитайте, что я пишу. А если этого будет мало, то проверьте на примере.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:06
Цитата Сообщение от Сыроежка Посмотреть сообщение
Вам следует сначала глубже изучить С++!
Да, ты прав. Этот пункт стандарта упустил.
0
Заблокирован
24.11.2011, 12:09  [ТС]
Цитата Сообщение от Deviaphan Посмотреть сообщение
Не корректный компилятор или настройки.
Ну я свою лошадку менять не буду, это однозначно.

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

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

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

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


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
struct Base
{
    Base(): a(333){}
    Base( const Base& istok) { std::cout << "Copy base\n"; }
    void View(){ std::cout << "base\n"; }
    int a;
};
 
 
struct Derrived:public Base
{
    Derrived():Base() {}
    Derrived( const Derrived& istok): Base(istok) { std::cout << "Copy Derrived\n"; }
    void View(){ std::cout << "Derrived\n"; }
};
 
 
int main()
{
    Derrived t;
    t.View(); //вывод: Derrived
    std::cout << "*******\n";
 
    Derrived* ptr = new Derrived;
 
    (static_cast<Base*>(ptr))->View();
    
    return 0;
};
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:10
Цитата Сообщение от ForEveR Посмотреть сообщение
Вы не правы.
Как не печально, но это косяк компилятора.)
У Саттера написано, что компилятор "продлевает" время жизни переменных, на которые есть константная ссылка. Но в стандарте пункт я пока не нашёл соответствующий.

Добавлено через 1 минуту
Цитата Сообщение от Bers Посмотреть сообщение
То есть... через указатель я могу интерпритировать тип, избежав копирования.
Потому что интерпретация и приведение типа это не одно и то же. Срезка происходит только при приведении типа.
0
В астрале
Эксперт С++
 Аватар для ForEveR
8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562
24.11.2011, 12:11
Deviaphan, Это не косяк компилятора был) Там в конструкторе копирования ничего не делалось
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:11
Цитата Сообщение от Bers Посмотреть сообщение
избежав копирования
Сейчас ты изменяешь не тип объекта, а тип указателя. Происходит копирование указателя.
0
Заблокирован
24.11.2011, 12:15
Цитата Сообщение от Deviaphan Посмотреть сообщение
Да, ты прав. Этот пункт стандарта упустил.
Вот вам элементарный пример, чтобы вы больше свою глупость не повторяли

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct A
{
   A( int i = 0 ) { p = new int( i ); }
   A( const A &rhs ) { p = new int( *rhs.p ); }
   ~A() { delete p; }
   int *p;
};
 
 
int main()
{
   const A &ra = A( 10 );
   A a1( 20 );
   
   std::cout << "*ra.p = " << *ra.p << std::endl;
}
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:16
Цитата Сообщение от Сыроежка Посмотреть сообщение
Вот вам элементарный пример
Я же уже сказал, что ты прав. Лучше пункт стандарта подскажи, не могу найти.(
0
Заблокирован
24.11.2011, 12:20  [ТС]
Цитата Сообщение от Deviaphan Посмотреть сообщение
У Саттера написано, что компилятор "продлевает" время жизни переменных, на которые есть константная ссылка. Но в стандарте пункт я пока не нашёл соответствующий.
Алена спп эту тему очень хорошо раскрыла в своём блоге. Она ж с этим самым агентом влияния от корпорации Зла знакома лично) И перессказывала то, что им, сотрудником мелкомягких говорил он.
Только линк на обсуждение не помню.

Добавлено через 1 минуту
Цитата Сообщение от Deviaphan Посмотреть сообщение
Сейчас ты изменяешь не тип объекта, а тип указателя. Происходит копирование указателя.
Да, я уже это понял.
Но копирование указателя значительно дешевле, чем копирование целого объекта.

Хотя обращение по такому указателю, все равно что работа со срезанным объектом.
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:22
Цитата Сообщение от Bers Посмотреть сообщение
Хотя обращение по такому указателю, все равно что работа со срезанным объектом.
Нет.)
0
Заблокирован
24.11.2011, 12:28
Цитата Сообщение от Deviaphan Посмотреть сообщение
Я же уже сказал, что ты прав. Лучше пункт стандарта подскажи, не могу найти.(
Надо смотреть раздел 12.2 "Temporary objects"
1
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:34
Цитата Сообщение от Сыроежка Посмотреть сообщение
12.2 "Temporary objects"
12.2/5 T3

Добавлено через 4 минуты
Теперь стало понятно, как и почему работал код, который недавно обсуждали на другой ветке. Там было
C++
1
const int & a = 123;
Теперь понятнее стало.)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
24.11.2011, 12:34

Xmega грабли
Так понимаю, мало кто в форуме xmega занимается, но вдруг кому полезно будет. Они, конечно, описаны в даташитах, но... Мелкие отличия...

Грабли с USART_FLAG_RXNE
Решил тут создать сообщение, чтобы поделиться ошибкой, что я ловил пол дня. Есть у арма регистр, куда падает приходящая инфа в УАРТ. ...

Наступлю на те же грабли?
Здравствуйте. Регистрировал доменное имя через jino.ru, а они согласно whois использовали R01-REG-RIPN. Потом выбрал другой хостинг....

Грабли с WM_DEVICECHANGE
Потратил кучу времени на изучение структур связанных с WM_DEVICECHANGE. Все работает как надо wParam == DBT_DEVICEARRIVAL , отлавливает...

Грабли malloc/free
С динамической памятью впервой работаю, от сюда и грабли Есть структура typedef struct { uint8_t data_size; uint8_t *data;...


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

Или воспользуйтесь поиском по форуму:
80
Ответ Создать тему
Новые блоги и статьи
Книги по геймдизайну, левел-дизайну, игровым механикам и проектированию игрового опыта
8Observer8 08.11.2026
Содержание блога | English Version 1. 2003 - 06 - Designing Virtual Worlds (1st Edition) Автор: Richard A. Bartle | Объем: 768 стр. Ссылки: Amazon . . .
Родители токсичные абьюзеры по отношению к своим детям ругают их оскорбляют не любят
zorxor 08.11.2026
Жить в атмосфере постоянных оскорблений, критики и холодности со стороны родителей — это тяжелое и болезненное испытание, которое оставляет глубокие эмоциональные раны. Токсичное и абьюзивное. . .
Как только я захожу к себе в свою комнату я сразу же с себя снимаю свои трусы. Ответ от Джемени ИИ.
zorxor 08.11.2026
Джемени, как только я захожу к себе в свою комнату я сразу же с себя снимаю свои трусы. Многие люди, возвращаясь домой, сразу переодеваются в максимально свободную одежду или вовсе предпочитают. . .
Паттерн очереди действий/команд (Action / Command Queue). Система очереди действий и кат-сцен с SDL3 Callbacks, cglm и OpenGL
8Observer8 07.11.2026
Содержание блога | English Version Руководство для начинающих по реализации паттерна очереди действий/ команд (Action / Command Queue) на C++ (C++20) с использованием официального SDL3 Callbacks. . .
Проверка полного ФИО в типовом справочнике
Maks 07.11.2026
Решение реализовано в конфигурации КА2, на примере справочника "КонтактныеЛицаПартнеров". Задача: Запрет инициалов. Не допускаеть использование сокращений вида "И. И. ", "И. И. ", "И И" и. . .
Про "Windows must die!"
kumehtar 04.11.2026
Знаете, чет накатило. . . . Когда-то это было почти боевым кличем: мол, нормальный человек сидит на DOS, OS/ 2 или Linux, а вот это вот - Must Die. А теперь человек произносит «мастдай» с усмешкой - и. . .
Красочные поля с номером страницы svg+background-image
russiannick 04.11.2026
Красочные поля с номером страницы не менее важны чем в эпоху бумажных книг. Посему спешу представить почтеннейшей публике этот важный инструмент который эти поля и формирует. В процессе разработки. . .
Вступаем в новую фазу генерации favicons, векторную.
russiannick 02.11.2026
Теперь нет нужды скрывать что используется простой svg. Исходный код изображения помещаем в поле и жмем кнопку. Исходник преобразуется в вид корректный для помещения в favicon или в url(). Нажатие. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru