Форум программистов, компьютерный форум CyberForum.ru

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

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 52, средняя оценка - 4.79
Bers
Заблокирован
23.11.2011, 01:05     Неочевидные грабли полиморфизма с++ #1
Наткнулся в интернете на любопытный код.
Спешу поделиться с сообществом.
Просто, что бы кто если не в курсе - узнал, и не попал на эти грабли:


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)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
23.11.2011, 01:05     Неочевидные грабли полиморфизма с++
Посмотрите здесь:

C++ Использование полиморфизма
Модификатор const Очередные грабли с++? C++
C++ Реализация полиморфизма
C++ иллюстрация полиморфизма
Виды полиморфизма C++ C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Bers
Заблокирован
24.11.2011, 11:34  [ТС]     Неочевидные грабли полиморфизма с++ #61
Цитата Сообщение от 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;
};
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:42     Неочевидные грабли полиморфизма с++ #62
Придумал поясняющий пример!
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
Сыроежка
Заблокирован
24.11.2011, 11:45     Неочевидные грабли полиморфизма с++ #63
Цитата Сообщение от 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;
То есть никакой речи об "интерпретации" памяти здесь не идет. Здесь имеет место быть два отдельных объекта.
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:49     Неочевидные грабли полиморфизма с++ #64
Цитата Сообщение от Bers Посмотреть сообщение
Глуповатый вопрос: почему строки написанного кода присутствуют в коде?
А ты глянь на ассемблерный листинг. Нет ни создания объекта t, ни привидения типа. Есть только код помещения текста в консоль.)

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

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

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

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 совершенно прав.
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 11:54     Неочевидные грабли полиморфизма с++ #66
Ура-ура, можно перестать биться головой об стенку.)

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

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

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

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

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

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

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

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

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

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

Надеюсь, третий раз вы эту чушь нести не будете. Если все же есть зуд, то сначала снова перечитайте, что я пишу. А если этого будет мало, то проверьте на примере.
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:06     Неочевидные грабли полиморфизма с++ #70
Цитата Сообщение от Сыроежка Посмотреть сообщение
Вам следует сначала глубже изучить С++!
Да, ты прав. Этот пункт стандарта упустил.
Bers
Заблокирован
24.11.2011, 12:09  [ТС]     Неочевидные грабли полиморфизма с++ #71
Цитата Сообщение от 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;
};
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:10     Неочевидные грабли полиморфизма с++ #72
Цитата Сообщение от ForEveR Посмотреть сообщение
Вы не правы.
Как не печально, но это косяк компилятора.)
У Саттера написано, что компилятор "продлевает" время жизни переменных, на которые есть константная ссылка. Но в стандарте пункт я пока не нашёл соответствующий.

Добавлено через 1 минуту
Цитата Сообщение от Bers Посмотреть сообщение
То есть... через указатель я могу интерпритировать тип, избежав копирования.
Потому что интерпретация и приведение типа это не одно и то же. Срезка происходит только при приведении типа.
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
24.11.2011, 12:11     Неочевидные грабли полиморфизма с++ #73
Deviaphan, Это не косяк компилятора был) Там в конструкторе копирования ничего не делалось
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:11     Неочевидные грабли полиморфизма с++ #74
Цитата Сообщение от Bers Посмотреть сообщение
избежав копирования
Сейчас ты изменяешь не тип объекта, а тип указателя. Происходит копирование указателя.
Сыроежка
Заблокирован
24.11.2011, 12:15     Неочевидные грабли полиморфизма с++ #75
Цитата Сообщение от 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;
}
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:16     Неочевидные грабли полиморфизма с++ #76
Цитата Сообщение от Сыроежка Посмотреть сообщение
Вот вам элементарный пример
Я же уже сказал, что ты прав. Лучше пункт стандарта подскажи, не могу найти.(
Bers
Заблокирован
24.11.2011, 12:20  [ТС]     Неочевидные грабли полиморфизма с++ #77
Цитата Сообщение от Deviaphan Посмотреть сообщение
У Саттера написано, что компилятор "продлевает" время жизни переменных, на которые есть константная ссылка. Но в стандарте пункт я пока не нашёл соответствующий.
Алена спп эту тему очень хорошо раскрыла в своём блоге. Она ж с этим самым агентом влияния от корпорации Зла знакома лично) И перессказывала то, что им, сотрудником мелкомягких говорил он.
Только линк на обсуждение не помню.

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

Хотя обращение по такому указателю, все равно что работа со срезанным объектом.
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:22     Неочевидные грабли полиморфизма с++ #78
Цитата Сообщение от Bers Посмотреть сообщение
Хотя обращение по такому указателю, все равно что работа со срезанным объектом.
Нет.)
Сыроежка
Заблокирован
24.11.2011, 12:28     Неочевидные грабли полиморфизма с++ #79
Цитата Сообщение от Deviaphan Посмотреть сообщение
Я же уже сказал, что ты прав. Лучше пункт стандарта подскажи, не могу найти.(
Надо смотреть раздел 12.2 "Temporary objects"
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.11.2011, 12:34     Неочевидные грабли полиморфизма с++
Еще ссылки по теме:

В чем смысл полиморфизма C++
C++ Использование свойств полиморфизма
Смысл использования полиморфизма C++

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

Или воспользуйтесь поиском по форуму:
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
24.11.2011, 12:34     Неочевидные грабли полиморфизма с++ #80
Цитата Сообщение от Сыроежка Посмотреть сообщение
12.2 "Temporary objects"
12.2/5 T3

Добавлено через 4 минуты
Теперь стало понятно, как и почему работал код, который недавно обсуждали на другой ветке. Там было
C++
1
const int & a = 123;
Теперь понятнее стало.)
Yandex
Объявления
24.11.2011, 12:34     Неочевидные грабли полиморфизма с++
Ответ Создать тему
Опции темы

Текущее время: 17:03. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru