Вы можете проверить, имеет ли ошибка тип WebException
, а затем проверить код ответа:
if (e.Error.GetType().Name == "WebException")
{
WebException we = (WebException)e.Error;
HttpWebResponse response = (System.Net.HttpWebResponse)we.Response;
if (response.StatusCode==HttpStatusCode.NotFound)
System.Diagnostics.Debug.WriteLine("Not found!");
}
или
try
{
// send request
}
catch (WebException e)
{
// check e.Status as above etc..
}
Стандарт C++ гарантирует следующее:
static_cast
луг указатель на и от void*
сохраняет адрес. Таким образом, в следующем, a
, b
и c
вся точка к тому же адресу:
int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);
reinterpret_cast
только гарантии, что при кастинге указателя на другой тип, и затем reinterpret_cast
это назад к исходному типу , Вы получаете исходное значение. Таким образом в следующем:
int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);
a
и c
содержат то же значение, но значение [1 111] является неуказанным. (на практике это будет обычно содержать тот же адрес как [1 112] и c
, но это не указано в стандарте, и это не может быть верно на машинах с более сложными системами памяти.)
Для кастинга к и от [1 114], static_cast
должен быть предпочтен.
Один случай, когда reinterpret_cast
необходимо, при взаимодействии через интерфейс с непрозрачными типами данных. Это часто происходит в API поставщика, над которыми программист не имеет никакого контроля. Вот изобретенный пример, где поставщик обеспечивает API для того, чтобы сохранить и получить произвольные глобальные данные:
// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();
Для использования этого API программист должен бросить их данные к VendorGlobalUserData
и назад снова. static_cast
не будет работать, нужно использовать reinterpret_cast
:
// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;
struct MyUserData {
MyUserData() : m(42) {}
int m;
};
int main() {
MyUserData u;
// store global data
VendorGlobalUserData d1;
// d1 = &u; // compile error
// d1 = static_cast<VendorGlobalUserData>(&u); // compile error
d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok
VendorSetUserData(d1);
// do other stuff...
// retrieve global data
VendorGlobalUserData d2 = VendorGetUserData();
MyUserData * p = 0;
// p = d2; // compile error
// p = static_cast<MyUserData *>(d2); // compile error
p = reinterpret_cast<MyUserData *>(d2); // ok
if (p) { cout << p->m << endl; }
return 0;
}
Ниже изобретенная реализация демонстрационного API:
// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
Читайте FAQ! При сдержании данных C++ C может быть опасным.
В C++, указатель на объект может быть преобразован в void *
без любых бросков. Но это не верно наоборот. Вам было бы нужно static_cast
для возвращения исходного указателя.
Значение reinterpret_cast
не определяется стандартом C++. Следовательно, в теории a reinterpret_cast
мог разрушить Вашу программу. В практике компиляторы пытаются сделать то, что Вы ожидаете, который должен интерпретировать биты того, что Вы являетесь передающими в том, как будто они были типом, к которому Вы бросаете. Если Вы знаете то, что компиляторы, которые Вы собираетесь использовать, делают с reinterpret_cast
, можно использовать его, но сказать, что это портативно , легло бы.
Для случая Вы описываете, и в значительной степени любой случай, где Вы могли бы рассмотреть reinterpret_cast
, можно использовать static_cast
или некоторая другая альтернатива вместо этого. Среди прочего стандарт говорит следующее о том, что можно ожидать static_cast
(В§5.2.9):
rvalue типа “pointer к условной цене void” может быть явно преобразован в указатель на тип объекта. Значение указателя типа на объект, преобразованный в “pointer к условной цене void” и назад в исходный тип указателя, будет иметь свое исходное значение.
Так для Вашего варианта использования, кажется довольно ясным, что комитет по стандартизации намеревался для Вас использовать static_cast
.
Одна причина использовать reinterpret_cast
состоит в том, когда базовый класс не имеет vtable, но производный класс делает. В этом случае, static_cast
и reinterpret_cast
приведет к различным значениям указателя (это было бы нетипичным случаем, упомянутым jalf выше ). Так же, как правовая оговорка я не заявляю, что это - часть стандарта, но реализация нескольких широко распространенных компиляторов.
Как пример, возьмите код ниже:
#include <cstdio>
class A {
public:
int i;
};
class B : public A {
public:
virtual void func() { }
};
int main()
{
B b;
const A* a = static_cast<A*>(&b);
const A* ar = reinterpret_cast<A*>(&b);
printf("&b = %p\n", &b);
printf(" a = %p\n", a);
printf("ar = %p\n", ar);
printf("difference = %ld\n", (long int)(a - ar));
return 0;
}
, Который производит что-то как:
& b = 0x7ffe10e68b38
= площадь 0x7ffe10e68b40
= различие 0x7ffe10e68b38
= 2
Во всех компиляторах я попробовал (MSVC 2015& 2017, лязгайте 8.0.0, gcc 9.2, ICC 19.0.1 - видят godbolt для последнего 3 ), результат эти static_cast
отличается от результата reinterpret_cast
2 (4 для MSVC). Единственный компилятор для предупреждения о различии был лязгом, с:
17:16: предупреждение: 'reinterpret_cast' от класса 'B *' к его основе при ненулевом смещении '*' ведет себя по-другому по сравнению с 'static_cast' [-Wreinterpret-базовый-класс]
константа* площадь = reinterpret_cast (& b);
^ ~~~~~~~~~~~~~~~~~~~~~~~
17:16:примечание: используйте 'static_cast' для корректировки указателя правильно в то время как константа upcasting
* площадь = reinterpret_cast (& b);
^ ~~~~~~~~~~~~~~~
static_cast
Один последний протест состоит в том, что, если базовый класс не имеет никаких элементов данных (например, int i;
) тогда, лязгают, gcc, и ICC возвращает тот же адрес для reinterpret_cast
что касается static_cast
, тогда как MSVC все еще не делает.
Вот вариант программы Avi Ginsburg, которая ясно иллюстрирует свойство reinterpret_cast
упомянутый Chris Luengo, flodin, и cmdLP: то, что компилятор рассматривает указанный ячейка памяти, как будто это был объект нового типа:
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
class A
{
public:
int i;
};
class B : public A
{
public:
virtual void f() {}
};
int main()
{
string s;
B b;
b.i = 0;
A* as = static_cast<A*>(&b);
A* ar = reinterpret_cast<A*>(&b);
B* c = reinterpret_cast<B*>(ar);
cout << "as->i = " << hex << setfill('0') << as->i << "\n";
cout << "ar->i = " << ar->i << "\n";
cout << "b.i = " << b.i << "\n";
cout << "c->i = " << c->i << "\n";
cout << "\n";
cout << "&(as->i) = " << &(as->i) << "\n";
cout << "&(ar->i) = " << &(ar->i) << "\n";
cout << "&(b.i) = " << &(b.i) << "\n";
cout << "&(c->i) = " << &(c->i) << "\n";
cout << "\n";
cout << "&b = " << &b << "\n";
cout << "as = " << as << "\n";
cout << "ar = " << ar << "\n";
cout << "c = " << c << "\n";
cout << "Press ENTER to exit.\n";
getline(cin,s);
}
, Который приводит к выводу как это:
as->i = 0
ar->i = 50ee64
b.i = 0
c->i = 0
&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i) = 00EFF978
&(c->i) = 00EFF978
&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c = 00EFF974
Press ENTER to exit.
Таким образом объект B создается в памяти как данные B-specific сначала, сопровождается встроенным объект. static_cast
правильно возвраты адрес встроенного объект и указатель, созданный static_cast
правильно, дают значение поля данных. Указатель, сгенерированный reinterpret_cast
обработки b
ячейка памяти, как будто это была плоскость объект, и поэтому когда указатель пытается получить поле данных, это возвращает некоторые данные B-specific, как будто это было содержание этого поля.
Одно использование reinterpret_cast
должно преобразовать указатель на целое число без знака (когда указатели и целые числа без знака являются тем же размером):
int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);