Заблокирован

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

23.11.2011, 01:05. Показов 16776. Ответов 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
Ответ Создать тему
Опции темы

Новые блоги и статьи
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет! За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru