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

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

23.11.2011, 01:05. Показов 16456. Ответов 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
Ответ Создать тему
Новые блоги и статьи
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
[EasyBuilder Pro] Памятка по разработке для панелей Weintek
ФедосеевПавел 26.08.2026
Памятка по разработке для панелей Weintek ВВЕДЕНИЕ Ранее, при реализации проектов основное внимание уделял разработке управляющей программы для контроллера, а панели оператора доставалось время. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru