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

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

14.04.2017, 00:08. Показов 5925. Ответов 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 / 1574 / 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 / 1574 / 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 / 1574 / 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,427
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 / 1574 / 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 / 1574 / 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 / 1574 / 169
Регистрация: 14.12.2014
Сообщений: 13,614
15.04.2017, 12:40
Цитата Сообщение от daslex Посмотреть сообщение
Адрес объектом не является.
Так у Прата нигде и не говорится что адрес является объектом. А вот значением указателя действительно является адрес. И именно адрес объекта присваивается указателю p2 с приведением типа. Именно это приведение и небезопасно потому что это указание компилятору "забей на несоответствие типов мне лучше знать что находится по исходному адресу который записан в pg" иначе такое присваивание компилятор посчитал бы ошибкой потому что типы несовместимы.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 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 / 1574 / 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
9005 / 4706 / 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
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru