const_cast

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
sarturbest
Сообщения: 13
Зарегистрирован: 16 июн 2009, 23:52

Здравствуйте, я здесь впервые :p :

Вот посмотрите код пожалуйста

Код: Выделить всё

#include <iostream>

int main()
{
	const int a=10;
	const int* cp=&a;

	int* p=const_cast<int*>(cp);	
	*p=15;

	std::cout<<a<<std::endl;
	
	return 0;
}
output: 10

Я просто не понимаю почему output: 10 а не 15
TEXHuK
Сообщения: 1
Зарегистрирован: 17 июн 2009, 10:39

Посмотри тут http://otvety.google.ru/otvety/thread?t ... 51d6037fde. Там примерно та же проблема рассмотрена
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

С помощью const_cast можно убрать const только с объектов, которые изначально константными не является. Применение const_cast для константного объекта согласно стандарта даёт undefined behaviour.

Кстати, очень интересный вопрос почему сделано именно так. Дело в том, что const объекты системных типов очень удобно оптимизировать полной подстановкой значения в месте использования. Такая оптимизация доступны не всегда, но в подавляющем числе случаев, в том числе, и в нашем примере, оптимизация возможна. Подстановка значения даёт несколько плюсов, в частности, и это самое главное, - это увеличение скорости. На константах удобно экономить, и именно поэтому стандарт говорит об undefined behavior, развязывая оптимизатору руки.

Что происходит конкретно в нашем случае очень просто можно увидеть разобрав дизассемблинг кода, сгенерированного VS 6.0:

Код: Выделить всё

11:       const int a=10;
00401798   mov         dword ptr [ebp-4],0Ah
12:       const int* cp=&a;
0040179F   lea         eax,[ebp-4]
004017A2   mov         dword ptr [ebp-8],eax
13:
14:       int* p=const_cast<int*>(cp);
004017A5   mov         ecx,dword ptr [ebp-8]
004017A8   mov         dword ptr [ebp-0Ch],ecx
15:       *p=15;
004017AB   mov         edx,dword ptr [ebp-0Ch]
004017AE   mov         dword ptr [edx],0Fh
16:
17:       std::cout<<a<<std::endl;
004017B4   push        offset @ILT+195(std::endl) (004010c8)
004017B9   push        0Ah
004017BB   mov         ecx,offset std::cout (0047c0c0)
004017C0   call        @ILT+250(std::basic_ostream<char,std::char_traits<char> >: :o perator<<) (004010ff)
004017C5   mov         ecx,eax
004017C7   call        @ILT+475(std::basic_ostream<char,std::char_traits<char> >: :o perator<<) (004011e0)
18:
19:       return 0;
004017CC   xor         eax,eax
Для того, чтобы было понятнее, я вывел листинг Debug сборки, то есть фактически жёсткая оптимизация уровня Release здесь отключена, но тем не менее, кое-какую оптимизацию компилятор провёл и в Debug.

Из листинга видно, что константа создаётся на стеке и в неё записывается значение 10 (mov dword ptr [ebp-4],0Ah), затем адрес константы загружается в переменую cp с помощью двух команд (lea, mov). Далее видим, как в переменную p загружается значение переменной cp через регистр ecx. В следующих двух строках мы наблюдаем, как в ячейку памяти, адрес который хранится в [ebp-0Ch] (это наше p), вносится число 15. Таким образом действительно в переменную [ebp-4] (это наше a) попадет число 15.

Теперь начинается самое интересное, а именно вывод a. Обратите внимание, что в качестве параметра в оператор << попадает не то, что хранится в [ebp-4], а соптимизированное значение этой переменной, подставленное на этапе компиляции, а следовательно то самое 10, которое было написанно в объявлении константы, а не 15, которое мы прописали в константу в runtime.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
sarturbest
Сообщения: 13
Зарегистрирован: 16 июн 2009, 23:52

Спасибо, все понятно :D , а как открыть дизассемблинг кода? У меня VS2005.

И еще один вопрос хотя не по теме.

Почему компилятор не дает возможности иницилизировать не константную статическую переменную класса, в классе? Есть в этом смысл?
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

&quot писал(а):Спасибо, все понятно, а как открыть дизассемблинг кода? У меня VS2005.
2005-ой студии под рукой нет, но, если не ошибаюсь, нужно найти в главном меню Debug -> Windows -> Disassembly. В 6-й студии несколько по-другому: View -> Debug Windows -> Disassembly.
&quot писал(а):Почему компилятор не дает возможности иницилизировать не константную статическую переменную класса, в классе? Есть в этом смысл?
Статическое поле не принадлежит объекту класса, а разделяется всеми объектами этого класса, поэтому в конструкторе, который вызывается для каждого объекта в отдельности, естественно инициализировать такое поле неправильно. Статики класса инициализируются следующим образом:

Код: Выделить всё

class A
{
   // ...
   static int sm_i;
   // ...
};

int A::sm_i = 0;
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
sarturbest
Сообщения: 13
Зарегистрирован: 16 июн 2009, 23:52

Это понятно, я имел ввиду почему нельзя делать так:

Код: Выделить всё

class A
{
   // ...
   static int sm_i=0;
   // ...
};
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

sarturbest, на будущее, используй С++ подсветку, о ней можно прочесть в мой подписи. Предыдущее сообщение уже подправил.

По вопросу. А почему нельзя сделать так? :)

Код: Выделить всё

0 = static int sm_i;
Ответы на твой и мой вопрос идентичны: потому, что таков синтаксис С++.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить