Перейти к содержимому

Static cast int c что это

  • автор:

Оператор static_cast

Преобразует выражение в тип идентификатора типа, основываясь только на типах, присутствующих в выражении.

Синтаксис

static_cast ( expression ) 

Замечания

В стандартном языке C++, проверка типа во время выполнения не выполняется, что обеспечивает безопасность преобразования. В C ++/CX выполняются проверки во время компиляции и во время выполнения. Дополнительные сведения см. в разделе Приведение.

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

Как правило, вы static_cast используете, когда требуется преобразовать числовые типы данных, такие как перечисления в инты или инты в с плавающей запятой, и некоторые типы данных, участвующие в преобразовании. static_cast преобразования не так безопасны, как dynamic_cast преобразования, так как static_cast не проверка типа времени выполнения. dynamic_cast Неоднозначный dynamic_cast указатель завершится ошибкой, в то время static_cast как возврат, как будто ничего неправильного; это может быть опасно. Хотя dynamic_cast преобразования являются более безопасными, dynamic_cast работает только на указателях или ссылках, а тип времени выполнения проверка является издержками. Дополнительные сведения см. в разделе dynamic_cast Оператор.

В следующем примере строка D* pd2 = static_cast(pb); небезопасна, поскольку D может иметь поля и методы, не входящие в B . Однако строка B* pb2 = static_cast(pd); является безопасным преобразованием, поскольку D всегда содержит все B .

// static_cast_Operator.cpp // compile with: /LD class B <>; class D : public B <>; void f(B* pb, D* pd) < D* pd2 = static_cast(pb); // Not safe, D can have fields // and methods that are not in B. B* pb2 = static_cast(pd); // Safe conversion, D always // contains all of B. > 

В отличие от dynamic_cast, проверка времени выполнения не выполняется при static_cast преобразовании pb . Объект, на который указывает pb , может не быть объектом типа D , и в этом случае использование *pd2 может привести ужасным последствиям. Например, вызов функции, являющейся членом класса D , но не класса B , может привести к нарушению прав доступа.

static_cast Операторы dynamic_cast перемещают указатель на всю иерархию классов. Тем не менее, static_cast использует исключительно информацию, указанную в заявлении приведения, и поэтому может быть небезопасной. Например:

// static_cast_Operator_2.cpp // compile with: /LD /GR class B < public: virtual void Test()<>>; class D : public B <>; void f(B* pb) < D* pd1 = dynamic_cast(pb); D* pd2 = static_cast(pb); > 

Если pb действительно указывает на объект типа D , pd1 и pd2 получат одно и то же значение. Также они получат одно и то же значение, если pb == 0 .

Если pb указывает на объект типа B , а не на полный D класс, то dynamic_cast будет известно достаточно, чтобы вернуть ноль. Однако, полагается на утверждение программиста, static_cast указывающее pb на объект типа D и просто возвращает указатель на этот предполагаемый D объект.

Следовательно, static_cast можно сделать обратное неявное преобразование, в этом случае результаты не определены. Программисту осталось убедиться, что результаты static_cast преобразования безопасны.

Это поведение также применяется к типам, отличным от типов класса. Например, static_cast можно использовать для преобразования из int в объект char . Однако результирующий char результат может не иметь достаточно битов для хранения всего int значения. Опять же, программисту осталось убедиться, что результаты static_cast преобразования безопасны.

Оператор static_cast также можно использовать для выполнения любого неявного преобразования, включая стандартные преобразования и определяемые пользователем преобразования. Например:

// static_cast_Operator_3.cpp // compile with: /LD /GR typedef unsigned char BYTE; void f() < char ch; int i = 65; float f = 2.5; double dbl; ch = static_cast(i); // int to char dbl = static_cast(f); // float to double i = static_cast(ch); > 

Оператор static_cast может явно преобразовать целочисленное значение в тип перечисления. Если значение типа целого не оказывается в диапазоне значений перечисления, получаемое значение перечисления не определено.

Оператор static_cast преобразует значение указателя NULL в значение null указателя целевого типа.

Любое выражение можно явно преобразовать в тип void оператором static_cast . Тип конечной пустоты может дополнительно включать const volatile атрибут или __unaligned атрибут.

Оператор static_cast не может отбросить const volatile атрибуты или __unaligned атрибуты. Сведения об удалении этих атрибутов см . в const_cast операторе .

C++/CLI: из-за опасности выполнения не проверка приведения на вершине перемещенного сборщика мусора, использование static_cast должно находиться только в критическом коде производительности, если вы уверены, что он будет работать правильно. Если вы должны использовать static_cast в режиме выпуска, замените его safe_cast в сборках отладки, чтобы убедиться в успешном выполнении.

Для чего нужен static_cast, как он работает и где его применяют?

У static_cast очень много различных применений. Его идея состоит в следующем: это ограниченный по мощи C-style cast. Ограничение нужно потому, что C-style cast может привести что угодно к чему угодно (ну, почти), и тем самым может скрыть ошибку. Например, вы можете случайно закастить const char* в char* , получив крэш в некоторых системах с аппаратной поддержкой const-памяти. static_cast вам такого не позволит.

Большую часть времени, когда вы хотите сделать явное преобразование типов (а я надеюсь, это бывает достаточно редко), вы хотите именно static_cast .

Формальный список всего, что умеет static_cast , очень большой, я приведу лишь наиболее важные вещи, которые он умеет (а также которые он не умеет):

  1. Преобразование указателя на родительский класс к указателю на дочерний класс. Объект по указателю обязан быть правильного дочернего класса, иначе undefined behaviour. Если вы не уверены и хотите проверить, тот ли подкласс у объекта, пользуйтесь dynamic_cast (он специально для этого предназначен).
  2. Преобразования между числовыми типами. int , long , char , unsigned int — все их можно кастить друг в друга при помощи static_cast .
  3. Можно закастить любое выражение в void . Результат будет вычислен и отброшен (но побочные эффекты, разумеется, выполнятся).
  4. static_cast может привести константу nullptr к любому типу-указателю. Обычно это не нужно и можно полагаться на неявное преобразование типов, но иногда (например, для выбора нужной перегрузки функции) такое может пригодится.
  1. Преобразование между указателями на в принципе несовместимые типы. Например, указатель на double нельзя привести к указателю на int . Для трюков с нарушением type safety пользуйтесь reinterpret_cast .
  2. Указатели на типы, а также сами типы с несовместимыми атрибутами const и/или volatile . Если вам необходимо нарушить const-корректность, пользуйтесь const_cast .
  3. Разумеется, вы не сможете привести указатель на функцию-член к указателю на обычную функцию, или указатель на код к указателю на данные. Для подобных грязных хаков пользуйтесь reinterpret_cast .

Ещё одной причиной использования static_cast (как и других C++-специфических преобразований типов) является лёгкость его поиска в исходниках, как глазами, так и поисковыми утилитами. Сишный каст (особенно его функциональную разновидность) очень легко пропустить в коде.

Для сравнения, «привычное» преобразование типов (C-style cast) равносильно следующей последовательности:

  1. const_cast .
  2. Если const_cast не может дать нужный результат, то static_cast (но с разрешённым преобразованием к недообъявленному типу)
  3. Если и так не выходит, то компилятор пробует в хвост к static_cast добавить const_cast .
  4. Если и это не получается, то reinterpret_cast .
  5. . а если не выйдет, то к нему дописывается const_cast .

Deep C++. Operation: static_cast

Как подсказывает название, static_cast преобразует выражения одного статического типа в объекты и значения другого статического типа. В соответствии со стандартом C++ (пункт 5.2.9/1-3):

«Результатом выражения static_cast(v) является результат преобразования выражения v к типу T . Если T является ссылкой, то результат lvalue; в противном случае — результат rvalue. В static_cast не должны определяться типы. Оператор static_cast не может снимать константность.

Выражение e может быть явно преобразовано к типу T используя static_cast следующим образом: static_cast(e) , если объявление “ T t(e); ” верно для некоторой воображаемой временной переменной t .

В противном случае static_cast должен выполнить одно из преобразований, указанных ниже. Никаких других преобразований не должно производиться явно при использовании static_cast.»

Это означает, что static_cast допускается, если:

  • объект типа, полученного после преобразования, может быть инициализирован значением исходного типа, или
  • приведение типа соответствует одному из исключений указанных далее в пункте 5.2.9 (которые я вскоре перечислю).

В этом пункте также упоминается, что static_cast «не снимает константность». Стандарт буквально имеет в виду, что оператор static_cast не может убрать спецификатор const . Например, static_cast не может преобразовать char const * в char * , хотя он может преобразовать char * в char const * .

cv- квалификацией принято называть добавление к типу спецификаторов const и/или volatile .

Если вы хотите убрать cv -квалификацию , то единственным подходящим оператором приведения типа будет const_cast . Я оставлю подробное обсуждение этого оператора до более основательного рассмотрения правил использования спецификатора const .

Примеры

Если даны объявления

struct B < operator int(); >; struct D : B < >; B b; float const f = 0;

то следующие два преобразования

static_castvoid *>(&b); // эквивалентно '(void *) &b;' static_castint>(b); // эквивалентно '(int) b;' and 'int(b);' 

являются допустимыми. Первое преобразование основано на стандартном неявном преобразовании типа B* к типу void* , тогда как второе неявно вызывает b.operator int() . Оба преобразования следуют общему правилу пункта 5.2.9/1 стандарта.

Как уже упоминалось выше, этот пункт определяет ряд исключений из общего правила. Эти исключения допускают преобразования

static_castint>(f); // эквивалентно '(int) f;' and 'int(f);' static_cast(b); // эквивалентно '(D &) b;' 

(Первый из двух примеров здесь не уместен, т.к., согласно стандарту, объект типа int может быть инициализирован значением типа float — прим. RSDN.)

даже если объекты получаемого типа не могут быть инициализированы значениями исходного типа.

В заключение, два выражения

static_castint const *>(&f); // OK static_castint *>(&f); // ошибка 

(С точки зрения стандарта, эти примеры недопустимы: static_cast не может приводить указатели несвязанных типов, каковыми являются int и float — прим. RSDN.)

Первое из них выполнится успешно, тогда как второе – нет. В обоих выражениях осуществляется попытка преобразовать указатель на переменную типа float в указатель на переменную типа int. Однако второе выражение также убирает спецификатор const, что не допускается стандартом. Второе преобразование можно осуществить более обычным способом

Неявное приведение типа

Пункт 5.2.9/1 допускает преобразование

T t = static_cast(e);
T t(e);

также является допустимой – т.е. если прямая инициализация возможна без приведения типа. Следовательно, объявления

long l = static_castlong>('x');
long l('x');

являются эквивалентными. Вам может показаться странным, что язык допускает такое избыточное приведение типа, т.к. оно ухудшает код без добавления какой-либо новой возможности.

Я же считаю полезным указывать все преобразования типов явным образом, даже когда это не необходимо. Преобразования типов опасны, вне зависимости от того, допускаются ли они неявно правилами языка, либо явно — операторами преобразования типа и конструкторами. Если вы будете прилежно выделять таким образом все преобразования типов, то сможете легче выявлять потенциальные источники проблем в дальнейшем.

Кто-то предлагал комитету стандартизации C++ добавить пятый оператор приведения типов ( implicit_cast ) для выделения преобразований, которые язык допускает и без этого. Комитет отклонил это предложение, возможно потому, что шаблон

templatetypename FROM, typename TO> inline TO implicit_cast(FROM const &x) < return x; >

является несложным в написании эквивалентом предложенного оператора . Используя этот шаблон, можно переписать приведенный ранее пример следующим образом:

long l = implicit_castlong>('x');

Если вам не нравится писать static_cast там, где не требуется явного приведения типа, подумайте над добавлением implicit_cast в вашу библиотеку и используйте его там, где возможно.

Явное приведение типа

Если бы оператор static_cast был разрешен только в ситуациях, где достаточно implicit_cast , он потерял бы всякий смысл. Однако, как я показал в предыдущих примерах, static_cast также позволяет производить явные преобразования типа там, где языком не разрешены неявные. Этими допустимыми явными преобразованиями являются:

  • Из любого типа к типу void
  • Из базового типа к ссылке на производный тип
  • Инверсия некоторых стандартных преобразований

К типу void

В соответствии со стандартом, любой тип выражения может быть преобразован к типу void с cv- квалификацией.

Это правило допускает все следующие выражения:

static_castvoid>(5); static_castconst void>("abc"); static_castvolatile const void>('x');

Вы можете удивиться: зачем вам может понадобиться приводить какой-либо тип к void , особенно учитывая, что вы не можете объявлять объекты типа void . На ум приходят две возможные причины.

Для начала рассмотрим шаблон

templatetypename R1, typename R2> R1 adapt(R2 (*f)()) < // . return static_cast(f()); >

adapt принимает в качестве параметра функцию f , которая не имеет параметров и возвращает тип R2 . При возврате управления функция adapt вызывает f() , преобразуя возвращаемый f тип R2 к типу, который возвращает сама функция adapt ( R1 ).

Для любознательных: я называю этот шаблон adapt , потому что его паттерн имеет сходство с адаптерами функций-объектов в STL.

Если вы инстанциируете шаблон функцией

int g(); adaptvoid>(g);

в результате будет создана специализация

void adapt(int (*f)()) < // . return static_castvoid>(f()); >

Поскольку static_cast может преобразовать к void , вы можете использовать один шаблон как для типа void , так и для других, отличных от void, типов.

Другой возможной мотивацией для static_cast служит явное игнорирование побочных эффектов выражения. Если вы вызываете функцию

int f();

без использования возвращаемого значения функции, то программисты, сопровождающие код в дальнейшем, будут гадать: собирались ли вы использовать возвращаемое значение, но случайно этого не сделали, или нет. Если же вы явно отбросите возвращаемое значение таким образом

static_castvoid>(f());

то другие программисты смогут быть более уверенными в ваших намерениях.

Базовый тип к ссылке производного типа

Если даны следующие типы

struct Base < >; struct Derived : Base < >;
Derived derived; Base &base = derived;

то стандарт позволяет явное преобразование

static_cast(base);

Стандарт утверждает, что результат этого приведения — lvalue типа Derived , но я думаю, что результат является – или должен быть – Derived & .

Хотя это приведение является допустимым, оно может привести к неприятностям. Рассмотрим похожий пример

Base base1; static_cast(base1);

Здесь, base1 в действительности является объектом типа Base , а не Derived . Это преобразование «обманывает» компилятор и приводит к неопределенному поведению программы.

Стандарт перечисляет другие ограничения времени компиляции на это преобразование:

  • Должно существовать неявное стандартное преобразование из Derived * в Base *
  • Base является невиртуальным базовым классом Derived
  • Derived имеет по крайней мере такую же cv- квалификацию , как и Base (т.е. вы можете добавить, но не убрать cv- квалификацию ).

Инверсия стандартных преобразований

Статья 4 стандарта перечисляет множество неявных стандартных преобразований:

  • Преобразование lvalues к rvalues
  • Преобразование массивов и функций к указателям
  • Добавление const и/или volatile
  • Преобразование и повышение разрядности интегральных типов и типов с плавающей точкой
  • Преобразования между указателями и указателями на члены класса
  • Преобразование к типу bool

Большинство этих преобразований адаптированы из C,и должны быть хорошо вам знакомы. Поскольку преобразования неявные, то они могут осуществляться без какого-либо явного указания о приведении типа. Например

char a[10]; char *p = a; // преобразование массива в указатель char const *s = p; // добавление спецификатора const void *v = p; // преобразование указателей float f = 123; // преобразование между интегральным числом и числом с плавающей точкой unsigned u = 123; // продвижение интегрального типа 

static_cast позволяет вам обратить эти преобразования, заставляя некоторые из них осуществляться противоположным образом. В частности, static_cast может обращать стандартные преобразования интегрального типа и типа с плавающей точкой, указателей и указателей на члены. В примере

char *p; void *v; v = p; // OK, неявное преобразование p = v; // error, нет неявного преобразования p = static_castchar *>(v); // OK, явное преобразование 

использование static_cast «подавляет» стандартные правила преобразований, вызывая преобразование, иначе невозможное.

static_cast также позволяет проводить обратные преобразования из:

  • Интегральных типов в перечисляемые
  • (возможно cv- квалифицированный ) Base * в (с не меньшей cv- квалификацией ) Derived *
  • (возможно cv- квалифицированный ) T Base:: * в (с не меньшей cv- квалификацией ) T Derived:: *
  • (возможно cv- квалифицированный ) void * в любой T *

Эти преобразования приводят к неопределенному поведению, если:

  • Интегральное значение выходит из диапазона перечисления
  • Base * не указывает на объект класса Derived
  • T Base:: * не указывает на член класса Derived
  • void * не указывает на объект класса T

Заметьте, что static_cast не обращает все неявные преобразования. В частности, static_cast не может преобразовать из:

  • Rvalues в lvalues
  • Указателей в массивы
  • Указателей в функции
  • Более ограниченного спецификатором const в менее ограниченный

Вы можете принудительно выполнить некоторые из этих преобразований с помощью других способов приведения типа. Остальные обращенные преобразования — такие как преобразование указателя обратно в массив — не могут быть выполнены никаким видом приведения типа.

Альтернатива нисходящему приведению типа

Многие из преобразований, допускаемых оператором static_cast , позволяют преобразовывать базовые классы в производные классы. Подобные преобразования называют нисходящими (downcast), так как они требуют спуска по иерархии наследования. Сравните их с противоположными им восходящими преобразованиями (upcast, от производных к базовым), которые язык позволяет делать неявно.

Нисходящее приведение типа небезопасно, т.к. оно подразумевает, что сущность статического типа, соответствующего базовому классу, в действительности имеет динамический тип производного класса. Если вы осуществляете нисходящее приведение типа с использованием static_cast , компилятор будет считать, что вы правы, даже если это не так. Если же в действительности это не так, результатом будет неопределенное — и подчас весьма неприятное — поведение программы.

Хотя я и не рассматриваю dynamic_cast в этой статье, я хотел бы упомянуть его в качестве альтернативы static_cast для нисходящего приведения типа. Принципиальным преимуществом dynamic_cast является то, что ваша программа проверяет ваше предположение во время исполнения. Если (предположительно) производная сущность в действительности не является объектом производного типа, то dynamic_cast либо вернет NULL (при преобразовании указателей), либо выбросит исключение (при преобразовании ссылок).

Я собираюсь подробно рассмотреть оператор dynamic_cast и ассоциированную с ним идентификацию типов во время выполнения (RTTI — Run-Time Type Identification) в будущих статьях.

Далее

В своей следующей статье я подобным образом рассмотрю reinterpret_cast и дам советы, когда использовать его либо static_cast .

Роберт Шмидт (Robert Schmidt) технический автор MSDN. Его другая основная писательская страсть — C/C++ Users Journal, в котором он является выпускающим редактором и ведет свою колонку. Его предыдущие этапы карьеры – радио ди-джей, дрессировщик диких животных, астроном, pool-hall operator, частный сыщик, разносчик газет и преподаватель колледжа.

Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.

Синтаксис: приведение типов данных в C и C++

Cи-стиль приведения типов данных доступен и языке C++, но считается не самодостаточным по сравнению с приведением типов в C++. Так как Си-стиль приведения типов не так точен, как C++-стиль приведения и не так заметен. Cи-стиль приведения типов данных может быть использован для преобразования любого типа в любой другой тип, при этом неважно насколько это небезопасное преобразование, например, преобразование целого числа в указатель типа int ). Казалось бы, такое преобразование невозможно, однако компилятор с нами не согласен, но выполнит это приведение. И каков получится результат. ему совершенно не важно. Смотрим синтаксис приведения типов данных согласно Си-стилю:

(type) val
  • type — тип данных к которому преобразуется значение val

причем тип данных обязательно указывается в круглых скобочках.

Рассмотрим следующий пример использования Си-стиля приведения типа данных int к типу double .

double res = (double)13 / 7;

Этот пример приводит int к типу double для того, чтобы при делении 13-ти на 7 избежать усечения результата из-за целочисленного деления. Если вы плохо знакомы с приведением типов данных, прочтите статью: Явное и неявное приведение типов данных.

static_cast — унарная операция приведения типов данных в С++

Операция static_cast доступна только в языке C++. static_cast может быть использована для преобразования одного типа в другой, но она не должна быть использована для выполнения недопустимого преобразования, например, преобразование значения в указатель или наоборот. Рекомендуется пользоваться операцией static_cast , нежели Cи-стилем приведения, потому что static_cast ограничивает недопустимое приведение типов и, следовательно — безопаснее.

Операция static_cast , грубо говоря, — это шаблон функции, в которой необходимо явно указать тип данных для преобразования, то есть задать параметр шаблона.

static_cast(value);

Итак, в треугольных скобочках указывается тип данных, к которому необходимо преобразовать значение value , которое стоит в круглых скобочках. Смотрим все тот же пример, который приводит int к типу double для того, чтобы при делении 13-ти на 7 избежать усечения результата из-за целочисленного деления.

double res = static_cast(13)/7;
dynamic_cast — унарная операция приведения типов данных в С++

Операция dynamic_cast доступна только в C++ и имеет смысл только, применительно к членам класса иерархии «полиморфных типов». Динамическое приведение типов данных может быть использовано для безопасного приведения указателя (или ссылки) на суперкласс, в указатель (или ссылку) на подкласс в иерархии классов. Если динамическое приведение типов — недопустимо, так как реальный тип объекта, указывает не на тот тип подкласса, приведение типов не выполнится.

Динамическое приведение указателя

При приведении указателя, в случае неудачи, dynamic_cast возвращает нулевой указатель NULL . Такое поведение обеспечивает быстрый способ определения, является ли данный объект частностью динамического типа.

Синтаксис указателя динамического приведения:

type *subСlass = dynamic_cast( objPtr );
Динамическое приведение ссылки

При приведении ссылочной переменной, не возможно вернуть указатель, в случае неудачи. Поэтому будет вызвано исключение std::bad_cast (из заголовочного файла ).

type subСlass = dynamic_cast( objReference );

Чтобы безопасно пользоваться динамическим приведением, все вызовы dynamic_cast должны быть обрамлены в блок Try/Catch .

const_cast — константное приведение типов данных

Операция const_cast доступна только в C++. Константное приведение используются, чтобы константную переменную преобразовать в неконстантную. При этом, константным становится возвращаемое значение операции const_cast , а не сама переменная.

const_cast(val);

Следующий пример преобразует константный указатель на символьную строку в неконстантный указатель на эту же строку.

void function(char *); // прототип функции с неконстантным параметром const char *string = "Sevastopol"; // константная строка function(const_cast(string));
reinterpret_cast — операция приведения типов данных

Операция reinterpret_cast доступна только в C++ и является наименее безопасной формой приведения типов данных в С++, она позволяет интерпретировать значение в другой тип данных. reinterpret_cast не должна быть использована для приведения иерархии классов или преобразования константных переменных.

reinterpret_cast( value );

Рассмотрим пример использования этой операции приведения, например, чтобы преобразовать целое значение в указатель, нужно написать следующее:

reinterpret_cast(777);

Более подробно о приведении типов данных читайте в статье: Явное и неявное преобразование типов данных.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *