Разъяснения потребности в C-стиле, дайте иное толкование, и броски константы

Действительно ли я прав в предположении, что C-стиль бросает (которым препятствуют), только reinterpret_casts? Используя последнего визуально поразительно и легок искать при поиске противных бросков, и следовательно это рекомендуется по броскам C-стиля?

При выбрасывании константы с помощью const_cast и пишущий в первоначально объект константы не определен, какова цель const_cast?

Примечание: Я знаю, что Bjarne справедливо осуждает операции кастинга, что они небезопасны, и даже идет вплоть до утверждения "Ужасной операции, должен иметь ужасную синтаксическую форму". и следовательно многословие кастинга операторов в C++. Таким образом, я попытаюсь минимизировать их использование. Обещание. :)

6
задан legends2k 18 September 2010 в 16:20
поделиться

5 ответов

Нет. Приведение C может выполнять эквивалент const_cast , static_cast , reinterpret_cast или их комбинацию. В случае, если этого было недостаточно, он также может сделать по крайней мере один небольшой трюк, который никакая комбинация новых приведений не может сделать вообще!

Вы можете использовать const_cast с определенными результатами, если исходная переменная определена без const , но все, что у вас есть, это указатель const или ссылка на этот объект . OTOH, если вы считаете, что у вас есть веская причина использовать const_cast , скорее всего, вам действительно стоит вместо этого поискать mutable .

Edit: Я полагаю, я должен был сказать это сразу, но приведение в стиле C может преобразовать в недоступный базовый класс. Например, рассмотрим что-то вроде:

[Изменить: я обновляю код так, чтобы он компилировался и (обычно) демонстрирует проблему. ]

#include <iostream>

class base1 {
public:
    virtual void print() { std::cout << "base 1\n"; }
};

class base2 {
public:
   virtual void print() { std::cout << "base 2\n"; }
};

class derived : base1, base2 {}; // note: private inheritance

int main() {    
    derived *d = new derived;
    base1 *b1 = (base1 *)d;    // allowed
    b1->print();    // prints "base 1"
    base2 *b2 = (base2 *)d;    // also allowed
    b2->print();    // prints "base 2"

//    base1 *bb1 = static_cast<base *>(d);  // not allowed: base is inaccessible

    // Using `reinterpret_cast` allows the code to compile.
    // Unfortunately the result is different, and normally won't work. 
    base1 *bb2 = reinterpret_cast<base1 *>(d);
    bb2->print();   // may cause nasal demons.

    base2 *bb3 = reinterpret_cast<base2 *>(d); 
    bb3->print();   // likewise
    return 0;
}

Код, использующий reinterpret_cast , будет компилироваться, но попытка использовать результат (по крайней мере, один из двух) вызовет серьезную проблему. reinterpret_cast принимает базовый адрес производного объекта и пытается обрабатывать его так, как если бы это был базовый объект указанного типа - и поскольку (максимум) один базовый объект может фактически существуют по этому адресу, пытаясь относиться к нему так, как другой может / вызовет серьезные проблемы.Изменить: в этом случае классы по существу идентичны, за исключением того, что они печатают, поэтому, хотя что-нибудь могло произойти, с большинством компиляторов оба последних двух распечатают «базу 1». Reinterpret_cast принимает все, что находится по этому адресу, и пытается использовать его как указанный тип. В данном случае я (пытался) сделать что-то безобидное, но видимое. В реальном коде результат, вероятно, будет не таким красивым.

Приведение в стиле C будет работать так же, как static_cast, если бы в коде использовалось публичное наследование вместо частного, то есть он знает, где в производном классе «живет» каждый объект базового класса, и корректирует результат, поэтому каждый Результирующий указатель будет работать, потому что он настроен так, чтобы указывать в нужном месте.

5
ответ дан 17 December 2019 в 00:08
поделиться

Помните, что приведение констант может воздействовать на что-то другое, кроме исходного идентификатора:

void doit(const std::string &cs)
{
    std::string &ms = const_cast<std::string &>(cs);
}

int main()
{
    std::string s;
    doit(s);
}

Итак, пока doit отбрасывает константу, в этом примере базовая строка не является константой, поэтому неопределенное поведение отсутствует.

Обновление

Хорошо, вот лучший пример того, когда использование const_cast не совсем бесполезно. Мы начинаем с базового класса с виртуальной функцией, которая принимает параметр const:

class base
{
public:
    virtual void doit(const std::string &str);
};

, и теперь вы хотите переопределить эту виртуальную функцию.

class child : public base
{
public:
    virtual void doit(const std::string &str)
    {
        std::string &mstr = const_cast<std::string &>(str);
    }
};

Из-за логики / структуры вашего кода вы знаете, что child :: doit будет вызываться только с неконстантными строками (а class base не находится под вашим контролем поэтому вы не можете его ни изменить, ни подпись child :: doit , потому что тогда он больше не будет переопределять base :: doit ).В этом случае можно безопасно отказаться от const.

Да, это рискованно. Возможно, когда вы это напишете, это правда, что выполнение никогда не достигнет child :: doit с неконстантной строкой, и код действителен. Но это может измениться либо при сохранении вашей программы, либо, возможно, когда вы перестроите и загрузите последнюю версию class base .

1
ответ дан 17 December 2019 в 00:08
поделиться

Нет, приведение типов в стиле C может действовать как reinterpret_cast s, const-cast s или static_cast s в зависимости от ситуации. Вот почему они не поощряются - вы видите приведение в стиле C в коде, и вам нужно посмотреть подробности, чтобы увидеть, что он будет делать. Например:

const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const
1
ответ дан 17 December 2019 в 00:08
поделиться

const_cast используется для удаления const из типа. Он также может удалить энергозависимый . Если объект действительно const , то результат не может быть записан и все равно будет иметь четко определенное поведение. Если, однако, он будет преобразован в const (путем передачи в функцию const T , то const_cast вернет его обратно в не const в порядке. (Я нашел дополнительную информацию здесь )

reinterpret_cast не может удалить const или volatile из типа.

см. Также

0
ответ дан 17 December 2019 в 00:08
поделиться

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

Основное преимущество, которое я вижу в использовании операторов приведения в стиле C ++, заключается в том, что они позволяют лучше выразить ваши намерения и позволяют компилятору по-прежнему выполнять некоторую проверку операции, которую вы просите выполнить, а не той операции, которую вы просите выполнить. размер подходит всем стиль C.

Что касается const_cast - вы часто попадаете в ситуацию, когда вы передаете объект через ссылку на константу просто потому, что API требует этого. Скажем, у вас есть функция X, которая задает строку в стиле C:

void X(const char *str) { ... }

Внутри этой функции вы передаете параметр функции C, которая ожидает char * , даже если она не меняет нить. Единственный способ учесть это - использовать const_cast str.

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

0
ответ дан 17 December 2019 в 00:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: