Я должен написать код для функции обратного вызова (это назовут из ATL, но это не действительно важно):
HRESULT callback( void* myObjectVoid )
{
if( myObjectVoid == 0 ) {
return E_POINTER;
}
CMyClass* myObject = static_cast<CMyClass*>( myObjectVoid );
return myObject->CallMethod();
}
здесь void*
как гарантируют, будет указателем на CMyClass
, так static_cast
законно. Мое беспокойство является кодом, должно быть максимально портативным (к более новым версиям Visual C++, по крайней мере). Таким образом, чтобы быть суперпараноидальным я склонен проверить CMyClass*
указатель также - я имею в виду что, если это оказывается пустым?
if( myObjectVoid == 0 ) {
return E_POINTER;
}
CMyClass* myObject = static_cast<CMyClass*>( myObjectVoid );
if( myObject == 0 ) {
return E_POINTER;
}
Действительно ли вторая проверка разумна? Действительно ли это возможно для static_cast
превратить ненулевого указателя в нулевого указателя?
static_cast может изменить значение указателя, если вы проводите кастинг между частями объекта с разными смещениями:
class A{ int x; }; class B{ int y; };
class C : A,B {};
C *c=new C();
B *b=c;
// The B part comes after the A part in C. Pointer adjusted
C *c2=static_cast<C*>(b);
// Pointer gets adjusted back, points to the beginning of the C part
Однако, "Значение нулевого указателя (4.10) преобразуется в значение нулевого указателя
тип назначения." (5.2.9-8), т.е. если c
является NULL
, то b
также является NULL
(и не корректируется) и таким образом c2
устанавливается в NULL
. Все это означает: если статическое приведение не-NULL myObjectVoid
дает NULL
, то значение myObjectVoid
было получено каким-то образом в обход системы типов. А это значит, что компилятор может отбросить вашу вторую проверку, потому что "этого все равно не может произойти".
Единственное изменение static_cast
, которое следует внести в указатель, - это выравнивание слов. Итак, теоретически myObjectVoid
указывает на последний байт в памяти, возможно, что он «выровнен» до 0, но я не считаю это реальной проблемой.
Нет. Если указатель ссылается на действительный объект и преобразование допустимо, то результат также будет относиться к действительному объекту, поэтому он не будет нулевым. Если один из них недействителен, значит, код неверен и результат не определен. Таким образом, единственный способ допустимого использования дать нулевой результат - это начать с нуля.
В конкретном случае преобразования между указателями на объекты и указателями на недействительные в стандарте сказано следующее (5.2.9 / 10):
Значение типа «указатель на объект» преобразовано в »указатель на
void
"и возврат к исходному типу указателя будет иметь исходное значение.
and this (4.10 / 3)
Результат преобразования «указателя на T» в «указатель на
void
» указывает на начало места хранения, где объект типа T находится
, поэтому исходный и конечный указатели на объекты будут одинаковыми, а указатель void
будет нулевым тогда и только тогда, когда указатели на объекты будут такими же.
Нет, вторая проверка не является разумной. Невозможно, чтобы static_cast
превратил ненулевой указатель в нулевой. Единственное, что static_cast
может изменить в предоставленном значении в вашем случае (будучи указателем) - это скорректировать адрес. В остальном же она занимается тем, что сообщает остальной части анализа типов компилятора, что результат выражения должен рассматриваться как целевой тип. Для указателя это означает, что разыменование находит правильный адрес в целевом объекте, а инкрементные и декрементные строки соответствуют размеру типа.