Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.93/15: Рейтинг темы: голосов - 15, средняя оценка - 4.93
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
1

Неявное приведение указателей на классы

05.04.2016, 10:28. Показов 3010. Ответов 15
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Всем привет!
Обнаружилась вот такая нестыковочка:
имеем интерфейсный класс IIn. И имеем класс-наследник ExtIn : public IIn
далее имеем такой код:
C++
1
2
3
ExtIn      ABCD(0, 0);                       // Создали наш класс
ExtIn*    pABCD         = & ABCD;       // Создали указатель на наш класс.
IIn*       p_2_ABCD    = & ABCD;       // Такой указатель тоже создается.
А далее не совсем понятная фигня:
C++
1
2
3
4
5
6
ExtIn**   ppABCD      = & pABCD;      // Такой указатель создается
IIn**      pp_2_ABCD = & p_2_ABCD; // И вот такой создается
IIn**      pp_3_ABCD = & pABCD;      // А вот такой компилятор не хочет создавать ни в какую.
// пишет, что не может конвертировать ExtIn** в IIn**, хотя нормально конвертирует одинарный указатель (ExtIn* в IIn*)
// а вот принудительное приведение он понимает:
IIn**      pp_4_ABCD = (IIn**) & pABCD;  // Компилирует. Все его устраивает.
Подскажите, чем обусловлена данная ситуация с двойными указателями?
Это где-то в стандарте описано или это какая-то особенность языка, о которой я еще не знаю?
Как такое поведение компилятора вообще объяснить?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.04.2016, 10:28
Ответы с готовыми решениями:

Запретить неявное приведение
Нужна функция с двумя формальными параметрами типа uint64_t, требуется запретить неявное приведение...

Неявное приведение типов
Объясните пожалуйста на пальцах, а то всё уже обгуглил. Где говорят - зависит от компилятора или...

Запретить неявное приведение возвращаемого значения
Есть функция, возвращающая объект класса, имеющего оператор приведения к uint64_t. Требуется...

Приведение указателей
Вопрос немного из другого раздела, но тем не менее, вопросы не по поводу WinApi, а поводу...

15
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
05.04.2016, 10:30 2
А вопрос то в чем?
0
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
05.04.2016, 10:32  [ТС] 3
Вопрос внизу:
Подскажите, чем обусловлена данная ситуация с двойными указателями?
Это где-то в стандарте описано или это какая-то особенность языка, о которой я еще не знаю?
Как такое поведение компилятора вообще объяснить?

Почему компилятор отказывается конвертировать двойные указатели без пинка?
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
05.04.2016, 10:41 4
Лучший ответ Сообщение было отмечено rikimaru2013 как решение

Решение

Цитата Сообщение от Pink_Pank Посмотреть сообщение
Такой указатель тоже создается.
Он подчиняется этому:
4.10 Pointer conversions
...
3. A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.
Цитата Сообщение от Pink_Pank Посмотреть сообщение
Подскажите, чем обусловлена данная ситуация с двойными указателями?
Pink_Pank, так указатели же разных типов.
2
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
05.04.2016, 10:50  [ТС] 5
Спасибо.

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

Не зря я когда-то любил макроассемблер... Любовь к нему все еще тлеет во мне... )))
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
05.04.2016, 12:42 6
Цитата Сообщение от Pink_Pank Посмотреть сообщение
Жаль, что такой могучий язык не может рекурсивно распарсить такую несложную конструкцию до начальных объектов...
Как вы себе это представляете? Учитывая, что (void*)pABCD!=(void*)p_2_ABCD.
На какую область памяти по вашему должен указывать pp_3_ABCD?
0
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
05.04.2016, 13:48  [ТС] 7
А вот взаимное расположение подобъектов базового и производного классов уже зависит от конкретной реализации компилятора. Так что если очень захотеть.. можно в космос полететь.

А вообще, обычно новые поля расположены в памяти за полями предка.
Исключение составляет виртуальное наследование. Поля виртуальных классов обычно располагают в самом конце.

Сейчас проверил в своем компиляторе - указатели равны. Да и вопрос не в равенстве, а в правильном конвертировании.

Добавлено через 5 минут
А еще при участии классов с виртуальными функциями в наследовании приоритет при размещении имеют именно они. Опять-таки в конкретном компиляторе
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
05.04.2016, 14:04 8
Цитата Сообщение от Pink_Pank Посмотреть сообщение
А вот взаимное расположение подобъектов базового и производного классов уже зависит от конкретной реализации компилятора.
И что? Если базовых классов несколько? В общем они не равны.
Похоже вы не понимаете как работают указатели. Пусть есть класс Foo. Пусть есть переменная Foo **********foo;
Тогда вызвать ее метод можно так: (**********foo).bar()Каждая звездочка - обращение по какому-то адресу в памяти в котором содержится следующий адрес.
Информации о типах указателей во время выполнения нет. Как вы собрались отличать, какая ячейка памяти на что указывает?
Вот есть ячейка памяти (соответствует цепочке указателей начинающейся красным). К какому смещению она приведет? Класса или его предка?

Добавлено через 7 минут
чтобы сделать так:
C++
1
2
3
class Foo : public Bar {...};
Foo **********foo=GetSome();
Bar **********bar=foo;
Нужна новая цепочка указателей, а не один указатель. Кто ее будет выделять?
1
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
05.04.2016, 17:08  [ТС] 9
Цитата Сообщение от avgoor Посмотреть сообщение
Похоже вы не понимаете как работают указатели.
Не надо мне приписывать те заслуги, которых у меня нет.
Цитата Сообщение от avgoor Посмотреть сообщение
Информации о типах указателей во время выполнения нет. Как вы собрались отличать, какая ячейка памяти на что указывает?
А как ВЫ отличаете во время выполнения, на что указывает указатель? О том, что это указатель, и на что он указывает, знает только компилятор.
Цитата Сообщение от avgoor Посмотреть сообщение
Вот есть ячейка памяти (соответствует цепочке указателей начинающейся красным). К какому смещению она приведет? Класса или его предка?
А как это происходит в случае одинарного указателя?
Эта ячейка приведет к данным, общим, как для предка, так и для наследника. Поэтому можете смело к ним обращаться. И именно от того, что эти данные одинаковы для базы и для наследника, а у наследника свой, дополнительный, набор данных возможна неявное преобразование указателя на наследника в указатель на предка. Но не наоборот.
Не вижу вообще проблемы сделать преобразование для множественного указания по аналогии с одиночным.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
05.04.2016, 17:28 10
Цитата Сообщение от Pink_Pank Посмотреть сообщение
Не вижу вообще проблемы сделать преобразование для множественного указания по аналогии с одиночным
Цитата Сообщение от Pink_Pank Посмотреть сообщение
Не надо мне приписывать те заслуги, которых у меня нет.
Видимо есть заслуги
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Foo
{
public:
    int foo;
}
class Bar : public Foo
{
public:
    int bar;
}
void f(Foo *** foo) {};
 
int main()
{
    Bar ***bar=GetpppBar();
    f(bar); //что сюда передавать?
}
0
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
05.04.2016, 19:06  [ТС] 11
Не вижу смысла дальше продолжать спор - Вы слишком брызжете слюной доказывая свою правоту.
Можете считать, что Вы правы.
0
18898 / 9856 / 2410
Регистрация: 30.01.2014
Сообщений: 17,299
05.04.2016, 19:43 12
Цитата Сообщение от Pink_Pank Посмотреть сообщение
Можете считать, что Вы правы.
Но ведь он на самом деле прав

Вот небольшой примерчик. Первая попытка - это правильное приведение (исходим из того, что в общем случае адреса наследника и базы не равны, что подтверждают как в общем правила C++, так и конкретный пример в частности), поэтому нам нужна временная переменная-указатель. Вторая попытка - это неверный каст, который исходит из предпосылок, что адреса равны. В итоге, во втором случае получаем закономерный некорректный результат.
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
#include <cstdio>
 
class A
{
public:
    int a;
 
    A(int a) : a(a) {}
};
 
class B
{
public:
    int b;
 
    B(int b) : b(b) {}
};
 
class C : public A, public B
{
public:
    int c;
 
    C(int c, int a, int b) : c(c), A(a), B(b) {}
};
 
int main()
{
    C c(1,2,3); // b == 3
    C * p = &c;
    B * r = &c;
    std::printf("%x - %x\n===\\\n", p, r);
    
    {
        B *  p1b = p;   // тот самый промежуточный указатель, о котором говорилось выше.
        B ** p2b = &p1b;
        
        std::printf("%x = %x\n", p2b, &p);
        
        std::printf("%d\n", (*p2b)->b);  //ok (b == 3)
    }
    {
        B ** p2b = (B **)&p;
        
        std::printf("%x = %x\n", p2b, &p);
        
        std::printf("%d\n", (*p2b)->b);  //fail
    }
}
Онлайн пример: http://rextester.com/TKLBS49114

С++ не может разрешить такое приведение через уровни по-умолчанию, т.к. в общем случае нам понадобиться временная переменная, чтобы хранить промежуточный адрес. И чем больше уровней указателя, тем больше таких переменных понадобится. Но проблема даже не в их наличии, а в том, что в общем виде нельзя решить задачу определения времени жизни этих переменных.

Не по теме:

PS. Это мой первый и последний пост в этой теме.

0
Вездепух
Эксперт CЭксперт С++
11696 / 6375 / 1724
Регистрация: 18.10.2014
Сообщений: 16,078
05.04.2016, 22:22 13
Лучший ответ Сообщение было отмечено Pink_Pank как решение

Решение

Цитата Сообщение от Pink_Pank Посмотреть сообщение
C++
1
IIn** pp_3_ABCD = & pABCD; // А вот такой компилятор не хочет создавать ни в какую.
Этот вопрос возникает часто в разных проявлениях. В частности, другая реинкарнация этой же темы - часто задаваемый вопрос о том, почему указатель int * можно неявно привести к const int *, а вот int ** к const int ** сам приводиться не хочет. "Ведь должно же тоже приводиться!".

На самом деле, дыра в логике вопрошающего тут точно такая же, как была бы вот в таком случае: тип int можно неявно привести к типу double, но это совсем на значит, что указатель int * можно неявно привести к указателю double *. Вот это последнее "несоответствие" почему-то никого не удивляет. Хотя на самом деле фундаментальная проблема с неявным приведением int * к double * - точно такая же, и как с неявным приведением int ** к const int **, и как с вашей попыткой привести ExtIn ** к IIn **.

Например, если бы приведение ExtIn ** к IIn ** было разрешено, мы бы могли сделать вот такой "финт ушами"

C++
1
2
3
4
5
6
7
class VasyaPupkin : public IIn {};
 
VasyaPupkin ya_vasya;
 
ExtIn *p_ext_in;
IIn **p_iin = &p_ext_in; // предположим это разрешено
*p_iin = &ya_vasya; // тут и так все в порядке
Теперь ваш указатель p_ext_in типа ExtIn * указывает на совершенно посторонний объект ya_vasya совершенно постороннего типа VasyaPupkin. И для того, чтобы вот так "сломать" систему типов языка вам не понадобилось сделать ни одного насильного приведения типов (!).

Это лишь иллюстративный пример. Ваше предположение, что из конвертируемости ExtIn * к IIn * должна следовать конвертируемость ExtIn ** к IIn ** - бессмысленно и безосновательно. Этот пример лишь иллюстрирует одно из очевидных проявлений этой бессмысленности.

Вам выше наприводили других примеров, также иллюстрирующих эту бессмысленность.
1
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
05.04.2016, 22:35  [ТС] 14
DrOffset, В общем-то, Ваш пример никак не противоречит сказанному мною. Переменная требуется всего одна. Независимо от уровней. Требуется она для промежуточных вычислений. И время жизни ее может быть сколь угодно мало. Вы же не задумываетесь о времени жизни значений в регистрах процессора, которые компилятор использует для хранения промежуточных значений.

Добавлено через 11 минут
TheCalligrapher, спасибо! Вот Ваш пример действительно оказался иллюстративным. ) замазали дыру. С Вами уже трудно не согласиться. )
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
06.04.2016, 00:51 15
Цитата Сообщение от Pink_Pank Посмотреть сообщение
Переменная требуется всего одна. Независимо от уровней. Требуется она для промежуточных вычислений.
Вы сильно заблуждаетесь. Cколько звездочек - столько требуется и переменных. Вам уже 2 раза об этом сказали.
C++
1
2
3
4
void f(int ****ptr)
{
    **ptr++; //Что мы будем здесь инкрементировать, если переменная одна.
}
И так в любом месте. Невозможно заранее сказать, какой уровень косвенности может измниться и где.
0
47 / 31 / 21
Регистрация: 04.04.2016
Сообщений: 209
06.04.2016, 09:07  [ТС] 16
Естесственно, что для хранения всех этих промежуточных адресов требуется отдельные ячейки памяти. И время жизни их зависит от того, как Вы эти ячейки и где объявите.
Я просто не понял, что Вы хотите сказать. Я подумал, что помимо участков памяти под указатели Вы хотите выделить кучу еще каких-то..
0
06.04.2016, 09:07
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
06.04.2016, 09:07
Помогаю со студенческими работами здесь

Приведение указателей
В функции в качестве параметра передаю указатель на один из самых базовых классов . Затем в функции...

Приведение указателей в стиле си
Здравствуйте, это наверное самый дурацкий вопрос но что значит скобочки в c++ т.е вот например дан...

приведение типов указателей
Задача у меня простая. Нужно побитно оперировать с числом unsigned int и на каких-то этапах...

Отличие приведение типов указателей
Чем отличаются при Base* a_ptr = new Derivered(); следующие строки: A) auto ptr =...


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

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru