Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.52/25: Рейтинг темы: голосов - 25, средняя оценка - 4.52
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886

Небезопасность приведения. Не понимаю Прата

14.04.2017, 00:08. Показов 6847. Ответов 61
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
О коде, который описан в книге, слегка переправлен мной:
Приведение типов не безопасно: оно присваивает адрес объекта базового класса (Grand) указателю на производный класс (Magnificent). После этого программа будет ожидать, что объект базового класса содержит свойства производного класса, что в общем случае неверно. К примеру, объект Magnificent может содержать данные-члены, которых нет в классе Grand.
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
#include <iostream>
 
using std::cout;
 
class Grand {
public:
    virtual void foo(){}
    virtual ~Grand(){}
};
 
class Superb : public Grand {
public: 
    void foo_grand(){}
};
 
class Magnificent : public Superb {
public:
    void foo_magnificent(){}
};
 
int main() {
    Grand *pg = new Grand;
    Grand *pm = new Magnificent;
 
    Magnificent *p2 = (Magnificent*)pg; //небезопасно
    
    p2->foo_magnificent();
    p2->foo_grand();
 
    delete pg;
    delete pm;
}
Я вижу только один объект базового класса: pg. Программа будет ожидать, что pg будет содержать свойства производного класса? - Что-то я такого не наблюдаю.

Проясните, пожалуйста, о чём речь.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
14.04.2017, 00:08
Ответы с готовыми решениями:

Небезопасность include, как быть?
почитал про небезопасность include, странно, как теперь быть то? вот мой индексный файл в котором много подключений, как можно сделать...

Прата С. 6.6
написал код, но после ввода первой структуры в цикле, выдает ошибку( //6.6.cpp #include &lt;iostream&gt; using namespace std; ...

С.Прата, задача 3.5
Гляньте, пожалуйста. Задача на преобразование целочисленных типов в с плавающей точкой. Вроде бы я решил: // запрос населения Земли и...

61
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
14.04.2017, 23:42
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от daslex Посмотреть сообщение
Напрямую зависит - конкретный объект интерпретируется как конкретный объект, а не как дяд Вася с хаты с краю.
Итерпритируется как конкретный объект то что лежит по конкретному адресу за счет того что интерпретирует его конкретный и неизменный в рантайме код. В данном примере показано как возникает рассогласование между тем что реально лежит по этому конкретному адресу и тем как его этот конкретный код интерпретирует.
Цитата Сообщение от daslex Посмотреть сообщение
Напрямую зависит - конкретный объект интерпретируется как конкретный объект, а не как дяд Вася с хаты с краю.
Ошибаетесь. Конкретный код интерпретирует объект находящийся по даденому ему адресу единственным образом. В данном примере коду дали адрес по которому лежит объект который имеет более другую структуру чем та которую ожидает код.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
14.04.2017, 23:54  [ТС]
И то что у объекта производного класса побочный эффект - это имело смысл написать по другому.
Благодаря GBaLog я понял, что хотел сказать Прата, самого Прату не понял. И Прата технически не прав. Вот и всё.

Добавлено через 1 минуту
Fulcrum_013, представьте себе, в том коде реально лежат два реальных значения.

Добавлено через 7 минут
И странно слышать слово рассогласование, где int интерпретируется как int, а double как double. Прекрасное согласование, но определённо опасное.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 01:21
Цитата Сообщение от daslex Посмотреть сообщение
И странно слышать слово рассогласование, где int интерпретируется как int, а double как double. Прекрасное согласование, но определённо опасное.
Так в том то и дело что в примере эта интерпретация не такая. т.е. фактически равносильная тому что int интерпретируется как double.

Добавлено через 3 минуты
Цитата Сообщение от daslex Посмотреть сообщение
представьте себе, в том коде реально лежат два реальных значения.
Сколько их там лежит не важно. Важно что по указателю p2 лежит объект типа Grand а код его интерпретирует как Magnificent

Добавлено через 4 минуты
Цитата Сообщение от daslex Посмотреть сообщение
И Прата технически не прав. Вот и всё.
Прат абсолютно прав. ключ к пониманию в понимании отличии преобразования
C++
1
2
double a;
int b= (int) a;
от преобразования
C++
1
2
double *a;
int *b= (int*) a;
В первом случае преобразовывается сам объект во втором только способ интерпретаци объекта кодом. Вот как раз второй случай Прат и описывает.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 01:32  [ТС]
int интерпретируется как int, без всяких сомнительных как double.
Всё то же самое
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
 
struct A{
    virtual ~A() {}
};
 
struct B : A
{
    int n=100;
};
 
int main()
{
    A * a = new A;
    B * b = (B*)a;
 
   
    system("PAUSE");
}
По указателю b лежит объект типа A, а компилятор тот объект интерпретирует как объект типа B.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <new>
 
using namespace std;
 
int main() {
 
   double *a= new double{200};
   int *b = (int*) a ;
 
   cout << (*a) << " == " << (*b) << "\n";
 
   delete a;
}
По указателю b лежит объект типа double, а компилятор тот объект интерпретирует как объект типа int.

Где же она, интерпретация, отличается?

Добавлено через 4 минуты
Прата хотя бы адрес объектом не называет, и на том спасибо.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 01:34
Цитата Сообщение от daslex Посмотреть сообщение
Где же она, интерпретация, отличается?
Дак и у прата то же самое. Именно такая ошибочная интерпретация и описана.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 01:36  [ТС]
Цитата Сообщение от daslex Посмотреть сообщение
объект базового класса содержит свойства производного класса
Не содержит == Не прав.
0
nd2
3438 / 2817 / 1249
Регистрация: 29.01.2016
Сообщений: 9,430
15.04.2017, 01:37
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Дак и у прата то же самое. Именно такая ошибочная интерпретация и описана.
Это называется: разговор зашёл в тупик.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 01:52  [ТС]
Я выше утрировано сократил, где равенство использовал. Где-то там, в началах истории, я более длинно формулировал эту мысль.

Добавлено через 10 минут
Вот так:
Цитата Сообщение от daslex Посмотреть сообщение
Я тут один говорю, что адрес - это адрес, вообще-то.
Все дружно пишут, что адрес - объект.
Нет, не объект. А то, что он объект - полная фигня.

И в примере это прослеживается.
Напрямую зависит - конкретный объект интерпретируется как конкретный объект, а не как дяд Вася с хаты с краю.
Базовый класс интерпретируется как базовый класс. Производный как производный и базовым классом не является, несмотря на то, что два разнотипных указателя в одной точке сходятся.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 04:00
чем вот это
C++
1
2
Grand *pg = new Grand;
Magnificent *p2 = (Magnificent*)pg;
отличается от вот этого:
C++
1
2
int *pg = new int(10);
double *p2 =(double*)pg;
принципиально ничем. И там и там в результате будет некорректная интерпретация данных О чем Прат и пишет.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 12:00  [ТС]
Прата вообще о другом пишет:
Цитата Сообщение от daslex Посмотреть сообщение
Приведение типов не безопасно: оно присваивает адрес объекта базового класса (Grand) указателю на производный класс (Magnificent). После этого программа будет ожидать, что объект базового класса содержит свойства производного класса, что в общем случае неверно. К примеру, объект Magnificent может содержать данные-члены, которых нет в классе Grand.
Добавлено через 17 минут
Вместо того, чтобы просто уже признаться, что имеет место маленький дефект в донесении информации, мы будем что-то доказывать, при этом не брезгуя противоречивостью самому себе.

Добавлено через 5 минут
Объясните мне, что некорректного в том, что int воспринимается как int, а double как double.
В правильной интерпретации double должен восприниматься не как double, а как какой-нибудь int, что ли?
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 12:08
daslex,
Цитата Сообщение от daslex Посмотреть сообщение
После этого программа будет ожидать, что объект базового класса содержит свойства производного класса, что в общем случае неверно.
- Абсолютно точно написано.
Для особо одаренны разжую:
Цитата Сообщение от daslex Посмотреть сообщение
После этого программа будет ожидать, что объект базового класса
который находится по адресу на который указывает указатель *p2
Цитата Сообщение от daslex Посмотреть сообщение
содержит свойства производного класса,
потому что тип указателя p2 Magnificent и соответственно интерпертироваться то что находится по указателю *p2
при доступе к нему через указатель p2 будет как Magnificent т.е. производный класс а значит программа будет ожидать что у того что находится по адресу на который указывает указатель p2 есть все свойства производного класса Magnificent
Цитата Сообщение от daslex Посмотреть сообщение
что в общем случае неверно. К примеру, объект Magnificent может содержать данные-члены, которых нет в классе Grand.
Да это действительно так.
Так где дефект донесения информации? Скорее дефект понимания того как работают указатели.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 12:25  [ТС]
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
#include <iostream>
 
using std::cout;
 
class Grand {
public:
    virtual void foo(){}
    virtual ~Grand(){}
};
 
class Superb : public Grand {
public:
    void foo_grand(){}
};
 
class Magnificent : public Superb {
public:
    void foo_magnificent(){}
};
 
int main() {
    Grand *pg = new Grand;
    Grand *pm = new Magnificent;
 
    Magnificent *p2 = (Magnificent*)pg; //небезопасно
 
    //Привет, я указатель pg, указываю на настоящий объект базового класса
    pg->foo_grand(); //программа от меня не ждёт ничего особенного, мне не дают в чужое тело лезть
                     //только воду баламутят, когда обо мне пишут
    delete pg;
    delete pm;
}
Указатель p2 указывая на некий производный объект, тип которого не является типом базовый класс, указывает на объект, который объектом базового класса не является и интерпретируется компилятором как объект производного класса.
Объект производного класса, берущий своё начало в объекте своего основного класса - это объект производного класса. Программа опирается на тип, что программе сказали, то она и тащит. Адрес объектом не является.
0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 12:40
Цитата Сообщение от daslex Посмотреть сообщение
Адрес объектом не является.
Так у Прата нигде и не говорится что адрес является объектом. А вот значением указателя действительно является адрес. И именно адрес объекта присваивается указателю p2 с приведением типа. Именно это приведение и небезопасно потому что это указание компилятору "забей на несоответствие типов мне лучше знать что находится по исходному адресу который записан в pg" иначе такое присваивание компилятор посчитал бы ошибкой потому что типы несовместимы.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
15.04.2017, 12:53
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
"забей на несоответствие типов мне лучше знать
Да. Это похоже на reinterpret_cast но только вручную. То есть, нам может быть не нужно увидеть верную интерпретацию, а мы сами хотим увидеть как оно выглядит по-байтово или какими-то другими кусками. Проблема TC в том, что определение типа:
-размер
-интерпретация (формат)
-допустимые операции
-диапазон возможных значений
он не понимает. Поэтому составной тип "указатель типа" он понять просто не в состоянии. Он не понимает, что интерпретация, это то что программа умеет делать только если ей сказать : "эти 4 байта это целое без знака" или "эти 4 байта это целое со знаком". И программа ожидает чего-то в смысле интерпретации только после указания информации о типе того, что нужно интерпретировать. Тогда непонятно и то, что никакая последовательность нулей и единиц в цепочке байт, сама по себе объектом не является. Не знает она, что кто-то может указать на её начало и попытаться проинтерпретировать ожидая, что там тип int, например.
0
15.04.2017, 13:07  [ТС]

Не по теме:

Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Так у Прата нигде и не говорится что адрес является объектом.
В этой теме меня об этом информировали. Не Прата.

К Прата нет никакой неприязни. Чем понятнее пишет человек, тем, как правило, больше у него технических неточностей. Плата за ясность.

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

0
 Аватар для Fulcrum_013
2083 / 1575 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 13:39
Цитата Сообщение от daslex Посмотреть сообщение
Объект как таковой - ни при чём. Иначе бы этот код, из поста 52, допускался бы компилятором. Программа бы могла ожидать от объекта чего-то необычного. Этот момент следствие технической неточности.
на самом деле и объект и интерпритация имеют значение. Вернее интерпретация должна совпадать с форматом данных которые находятся по тому адресу который обрабатывает программа. Иначе будет бяда-бяда-разорение.
Кстати компилятор очень так даже нехило следит за тем чтобы рассогласований не было. Причем следит настолько нехило что у программиста есть только два способа организовать несоответствие - либо прямо указать компилятору "забей на несогласование" либо ошибиться с арифметикой указателей. При этом обойти указания "забей" в принципе мало возможно, т.к. в ООП может понадобится приведение указателя на предка к указателю на потомка, причем обойти такое приведение может оказаться слишком сложно а то и невозможно. Именно поэтому Прат и концентрирует внимание на этом аспекте - именно здесь можно прострелить себе ногу. Причем делает это абсолютно точно в каждой букве. Через годик другой перечитаете поймете что Прат описывает все с буквоедской точностью. Понимание работы указателей это кстати как уметь ходить или ездить на велосипеде не падая - пока шишек не понабиваешь не поймешь как это работает и не научишься. Вот когда при разработке кода об указатели достаточно шишек понабиваете тогда и поймете что у Прата все абсолютно точно.
0
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 13:53  [ТС]
Невозможно порой указатель на предка к указателю на потомка привести? Это нужно обходить?
Я не понимаю Ваш язык.

Это имею в виду я.
ООП, виртуальный метод в классе-предке дают гарантию безопасности приведения указателя на предка к указателю на потомка. Точно также гарантировано безопасно и обратное приведение.

А что Вы имели в виду?
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
15.04.2017, 13:59
Цитата Сообщение от daslex Посмотреть сообщение
Иначе бы этот код, из поста 52, допускался бы компилятором.
Нет. Этот код не относится к примеру Пратты. Вы создали указатель на класс в котором нет метода который пытаетесь вызвать. Общее замечание. Не понимая чего-либо, старайтесь рассматривать самые простые случаи. Зачем иерархия из трёх уровней? Зачем столько указателей?
Пример вам проиллюстрировал GbaLog- и там ошибка выполнения с попыткой обратиться куда попало. Тут программа ожидала, не того что получила.
В терминах вашего кода это:
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
#include <iostream>
 
using std::cout;
 
class Grand {
public:
    
        virtual ~Grand(){}
};
 
class Superb : public Grand {
public:
    void foo_grand(){}
};
 
 
int main() {
    Grand *pg = new Grand;
    Superb *pm = (Superb*)pg;
 
   pm->foo_grand();
 
    delete pg;
    delete pm;
}
1
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
15.04.2017, 14:10  [ТС]
Это Вы всё принять не хотите, что объект - это место в памяти. С моей точки зрения программа ничего не знает о том, что лежит на каком-то адресе, её обязательно информировать, ибо С++ строго типизированный язык программирования. Если мы информируем программу, что по адресу ул. Советская, д6 живёт дядя Федя, то программа ждёт, что там живёт дядя Федя, и любые указания, которые даются программе, относятся к дяде Феде. Если мы информируем программу, что по адресу ул. Советская, д6 живёт тётя Люся, то программа ждёт, что там живёт тётя Люся, и любые указания относятся к тёте Люсе. Дом один - объекты разные.

А то, что программа приходит на адрес, и вместо ожидания дяди Феди видит перед собой тётю Люсю, программу немного сбивает с толку, программа сообщает тёте Люсе, чтобы она немедленно трансформировалась в дядю Федю, потому что здесь живёт дядя Федя. Тётя Люся трансформируется в дядю Федю, и что-то ломается. У тёти Люси нет некоторых деталей.

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

Вот так точно. А то, что программа чего-то там от объекта базового класса ждёт - это - неточно.
0
31 / 31 / 6
Регистрация: 23.10.2014
Сообщений: 107
15.04.2017, 14:49
Лучший ответ Сообщение было отмечено daslex как решение

Решение

Цитата Сообщение от daslex Посмотреть сообщение
Программа ничего не ждёт от объекта, она берёт объект и всё, что есть в объекте, который как раз объект базового типа, переносит в объект другого типа. На этом роль объекта базового типа кончается. Дальше уже существует объект производного типа, который сформирован неправильным образом.
Где? https://wandbox.org/permlink/i0j9bq4IM1Th0uRt
По адресу false_b как был объект типа A так и остался.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
15.04.2017, 14:49

Прата задача 6.9
Всем доброго времени суток, подскажите как сделать что бы программа в строке 32 принимала имена с пробелами. Если открываю файл без...

C++ - Прата задача 6.9
Подскажите пожалуйста в чем ошибка?не обрабатывается условие неправильного ввода имени файла. #include &lt;iostream&gt; #include...

Прата, задача 2, глава 6
Доброго времени суток. Учу программирования по книжке Прата. И вот такая задача: &quot;Напишите программу, которая считывает до десяти...

Прата, аргументы по умолчанию
Тема - аргументы по умолчанию. Приведён код #include &lt;iostream&gt; const int ArSize = 80; char * left(const char * str, int n = 1); ...

Прата -> Шлее = PROFIT?
Изучением программирования занялся от безделья. Путём гугления выбрал С++. В качестве первой книги - Стивен Прата &quot;Язык...


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

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
сукцессия 30. Фосфорный и азотный замки по "честному" закону лимитирования Либиха
anaschu 16.07.2026
Основные достижения за последние дни 1. Механизм отложенного высвобождения фосфора Добавлен специальный пул для медленного разложения меланизированных гиф ЕСМ Реализована разная скорость. . .
Запрет пометки на удаление и отмены проведения
Maks 16.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "ПутевойЛист" разработанного в КА2. Задача: запретить помечать на удаление и отменять проведения путевого листа при условии, если он сдан. . .
Как надо было делать правильно - вообще не слушать ИИ, его дело - текст.
Hrethgir 15.07.2026
А вообще надо было делать не так, чтобы не исправлять ошибку https:/ / www. cyberforum. ru/ blogs/ 223907/ 10996. html , правильно было : удалил кнопку, а потом добавил её. Всё это стресс конечно. Почему?. . .
Знаете, от чего появляется стресс?
kumehtar 15.07.2026
Стресс появляется оттого, что вы, зная как поступить правильно, делаете наоборот. Ларри Уингет
Нас бьют, а мы крепчаем? Какая чушь!
kumehtar 15.07.2026
Вот это вот бытующее мнение, что человек меняется только под действием проблем, всегда считал вредоносным. Поверь, от проблем у людей появляются не изменения, а нервное расстройство и ПТСР. А. . .
сукцессия 29. Переход от одних деревьев на другие делать более или менее вероятностным?
anaschu 12.07.2026
Насколько смена типов микоризы — исключительное событие в двухвековой сукцессии? Оценка вероятности в пространстве параметров В текущей версии модели успешно реализован ключевой механизм. . .
сукцессия 27. Думаю, как переделывать уже написанную статью с планами на сукцессию.
anaschu 12.07.2026
Анализ соответствия модели требованиям Реализованные компоненты: Механизм закисления почвы через протонную помпу Конкуренция между типами микориз pH как триггер сукцессии C/ P соотношение. . .
Сукцессия 26. Мат модель создана.
anaschu 12.07.2026
Модель смены растительных сукцессий посредством управления грибами работает внутри небольшой ячейки почвы, восстанавливающейся после пожара, где ненадолго бывшее царство хвойных снова захватили. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru