Немного сомнений по поводу приведения операторов в C ++ - PullRequest
6 голосов
/ 05 ноября 2010

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

  1. Как работает reinterpret_cast Какова магия (внутренняя реализация), которая позволяет reinterpret_cast работать?
  2. Какобеспечить безопасность при использовании reinterpret_cast?Насколько я знаю, это не гарантирует безопасного приведения, так какие меры предосторожности предпринять при использовании reinterpret_cast?
  3. Каково практическое использование этого оператора.Я действительно не сталкивался с этим в моем профессиональном опыте программирования, в котором я не мог обойтись без использования этого оператора. Любые практические примеры, кроме обычного int * to char *, будут очень полезны и оценены.

Еще один вопрос, касающийся операторов приведения в целом:
Операторы приведения (static_cast, dynamic_cast, const_cast, reinterpret_cast) все называются Operators, т. Е. Насколько я понимаю, так жеправильное утверждение, чтобы сделать это casting operators cannot be overloaded unlike most other operators (я знаю, что не все операторы могут быть перегружены, и я знаю, чего не может быть (кроме вопроса, который я прошу, пожалуйста, воздержитесь от этого), просто у меня было это сомнение, так как ониоператоры, что стандарт говорит об этом?

Ответы [ 9 ]

4 голосов
/ 05 ноября 2010
  1. Нет магии.reinterpret_cast обычно просто означает (по крайней мере, попытайтесь) обработать то, что вы найдете по этому адресу, как если бы это был тип, который я указал.Стандарт определяет достаточно мало о том, что он делает, чтобы он мог отличаться от этого, но это редко (если вообще когда-либо) действительно.
  2. В некоторых случаях вы можете получить безопасность от чего-либокак дискриминационный союз.Например, если вы читаете сетевые пакеты и читаете достаточно, чтобы увидеть, что вы получили TCP-пакет, то вы можете (достаточно) безопасно выполнить reinterpret_cast от IPHdr до TCPHdr (или что-то еще)имена, которые вы использовали).Хотя компилятор не будет (опять же, обычно) делать многое - от вас зависит какая-либо безопасность в реализации и применении.
  3. Я использовал код, описанный в 2), для работы с различными типами сетей.пакеты.

Ваш последний вопрос: вы можете перегрузить приведение для класса:

class XXX { 
public:
    operator YYY() { return whatever; }
};

Хотя это может быть использовано для преобразований в целом - независимо от того, выполняется ли это static_cast,Приведение в стиле C или даже неявное преобразование.C ++ 0x позволяет вам добавлять квалификатор explicit, чтобы он не использовался для неявных преобразований, но по-прежнему нет способа провести различие между статическим преобразованием в стиле C_.

2 голосов
/ 05 ноября 2010

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

"Как [это] работает?" Ну, как вы можете догадаться из названия, цель состоит в том, чтобы просто изменить интерпретацию битового паттерна, возможно, расширения или короткого замыкания в зависимости от ситуации. Это своего рода изменение типа, когда битовый шаблон остается неизменным, но изменяется интерпретация и, следовательно, концептуальное значение. И это в отличие от некоторого изменения типа, когда концептуальное значение сохраняется (например, int преобразуется в double), в то время как битовый шаблон изменяется по мере необходимости для сохранения концептуального значения. Но большинство случаев reinterpret_cast имеют определенный эффект реализации, поэтому в этих случаях ваш компилятор может делать все, что ему захочется - не обязательно сохраняя битовый шаблон - до тех пор, пока это задокументировано.

«Как обеспечить безопасность» Это значит знать, что делает ваш компилятор, и избегать reinterpret_cast. : -)

«Что такое практическое использование». В основном речь идет о восстановлении информации о типах, которая была потеряна в C-ориентированном коде, где указатели void* используются для эмуляции полиморфизма.

Приветствия & hth.,

2 голосов
/ 05 ноября 2010

reinterpret_cast обычно позволяет вам делать очень плохие вещи. В случае приведения указателя это разрешит приведение из одного типа в другой, у которого нет абсолютно никаких оснований предполагать, что это должно работать . Это все равно что сказать "поверь мне, я действительно хочу это сделать". Что именно это делает, непредсказуемо от одной системы к другой. В вашей системе он может просто копировать битовые шаблоны, где, как и в другой, он может преобразовывать их некоторым (потенциально полезным) способом.

, например

class Foo {
    int a;
};

class Bar {
    int a;
};

int main() {

  Foo a;

  // No inheritance relationship and not void* so must be reinterpret_cast 
  // if you really want to do it
  Bar *b = reinterpret_cast<Bar*>(&a);

  char buffer[sizeof(Bar)];

  Bar *c = reinterpret_cast<Bar*>(buffer); // alignment?

}

С радостью позволю вам сделать это, независимо от сценария. Иногда, если вы делаете низкоуровневые манипуляции с вещами, это может быть тем, чем вы хотите заниматься. (Представьте себе char * приведения буфера к определенному пользователем типу)

Потенциальные ловушки огромны, даже в простейшем случае, например, в буфере, где выравнивание вполне может быть проблемой.

При использовании dlsym () в Linux полезно иметь возможность приводить void* к указателю на функцию, что в C ++ в противном случае является неопределенным поведением. (Некоторые системы могут использовать отдельные адресные пространства или указатели разных размеров!). Это можно сделать только с помощью reinterpret_cast в C ++.

1 голос
/ 06 ноября 2010

Одно «практическое» использование reinterpret_cast.

У меня есть класс, в котором участники не предназначены для чтения.Пример ниже

class ClassWithHiddenVariables
{
private:
    int a;
    double m;
public:
    void SetVariables(int s, int d)
    {
        a = s;
        m = d;
    }
}; 

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

Теперь по какой-то причине я хочу видеть членов в одной определенной части.Однако я не хочу трогать существующий класс. Поэтому нарушите правила следующим образом.

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

class ExposeAnotherClass
{
public:
    int a_exposed;
    double m_exposed;
};

Если вы хотите видеть члены объекта ClassWithHiddenVariables, используйте reinterpret_cast для приведения к ExposeAnotherClass.Пример следует

ClassWithHiddenVariables obj;
obj.SetVariables(10, 20.02);    
ExposeAnotherClass *ptrExposedClass;
ptrExposedClass = reinterpret_cast<ExposeAnotherClass*>(&obj);  
cout<<ptrExposedClass->a_exposed<<"\n"<<ptrExposedClass->m_exposed;

Я не думаю, что такая ситуация когда-либо происходит в реальном мире.Но это всего лишь объяснение reinterpret_cast, который рассматривает объекты как битовые комбинации.

1 голос
/ 05 ноября 2010
  1. reinterpret_cast работает только для указателей.Это работает так, что он оставляет значение указателя в покое и изменяет информацию о предполагаемом типе.Он говорит: «Я знаю, что эти типы не эквивалентны, но я хочу, чтобы вы просто притворились, что теперь это указатель на T2».Конечно, это может вызвать любое количество проблем, если вы используете указатель T2 и он не указывает на T2.

  2. Существует очень мало гарантий относительно reinterpret_cast, поэтомучтобы так избежатьНа самом деле вам разрешено только приводить из T1 в T2, а затем обратно в T1, и вы знаете, что, учитывая некоторые предположения, конечный результат будет таким же, как вы начали.* Единственное, о чем я могу думать, это приведение char * к unsigned char *.Я знаю, что базовое представление одинаково в моей реализации, поэтому я знаю, что приведение является безопасным.Я не могу использовать статическое приведение, потому что это указатель на буфер.В действительности вы найдете очень мало законного использования reinterpret_cast в реальном мире.

Да, они операторы.AFAIK, вы не можете переопределить их.

0 голосов
/ 05 ноября 2010

Я много использовал reinterpret_cast в программировании Windows.При обработке сообщений используются параметры WPARAM и LPARAM, которые необходимо привести к правильным типам.

0 голосов
/ 05 ноября 2010

reinterpret_cast довольно эквивалентно касту в стиле C. Это ничего не гарантирует; он позволяет вам делать то, что вам нужно, в надежде, что вы знаете, что делаете.

Если вы хотите обеспечить безопасность, используйте dynamic_cast, поскольку это то, что он делает. Если приведение не может быть завершено безопасно, dynamic_cast возвращает NULL или nullptr (C ++ 0x).

Приведение с использованием «операторов приведения», таких как static_cast, dynamic_cast и т. Д., Не может быть перегружено. Прямые преобразования могут, например:

class Degrees
{
public:
    operator double() { ... }
};
0 голосов
/ 05 ноября 2010

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

Почти, но не точно.Например, вы не можете использовать reinterpret_cast для приведения const int* к int*.Для этого вам нужно const_cast.

Как работает reinterpret_cast, Что такое магия (внутренняя реализация), которая позволяет reinterpret_cast работать?

Там нет магиисовсем.В конечном итоге, все данные являются только байтами.Система типов C ++ - это просто уровень абстракции, который сообщает компилятору, как «интерпретировать» каждый байт.A reinterpret_cast аналогичен обычному C-приведению в том смысле, что он просто говорит «к черту систему типов: интерпретируйте эти байты как тип X вместо типа Y!»

Как обеспечитьбезопасность при использовании reinterpret_cast?Насколько я знаю, это не гарантирует безопасного приведения, так какие меры предосторожности предпринять при использовании reinterpret_cast?

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

Каково практическое использование этого оператора.Я действительно не сталкивался с этим в своем профессиональном опыте программирования, в котором я не мог обойтись без использования этого оператора. Любые практические примеры, кроме обычного int * to char *, будут очень полезны и оценены.

У него много применений, но обычно они несколько «продвинуты».Например, если вы создаете пул памяти связанных блоков и сохраняете указатели для освобождения блоков на самих блоках, вам потребуется reinterpret_cast блок из T* в T**, чтобы интерпретировать блок какуказатель на следующий блок, а не на сам блок.

0 голосов
/ 05 ноября 2010

reinterpret_cast говорит компилятору «заткнись, это переменная типа T *», и не имеет никакой безопасности , если только это не действительно переменная типа T *. В большинстве реализаций просто ничего не делается - одно и то же значение в переменной передается получателю.

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

...