Нужны пояснения в стиле C, переосмысление и приведение const

StackOverflow https://stackoverflow.com/questions/2346616

Вопрос

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

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

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

Это было полезно?

Решение

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

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

Редактировать:Полагаю, мне следовало сказать это сразу, но приведение в стиле 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_casts будет скомпилирован - но попытка использовать результат (по крайней мере, одного из двух) вызовет серьезную проблему.В reinterpret_cast принимает База адрес производного объекта и пытается обработать его так, как если бы это был указанный тип базового объекта - и поскольку (самое большее) один базовый объект может фактически существовать по этому адресу, попытка обработать его как другой может / вызовет серьезные проблемы.Редактировать:В этом случае классы по существу идентичны, за исключением того, что они печатают, поэтому, хотя что-либо мог бы случается, что в большинстве компиляторов оба последних двух выводят "base 1".reinterpret_cast принимает все, что оказывается по этому адресу, и пытается использовать его в качестве указанного типа.В данном случае я (попытался) заставить это сделать что-то безобидное, но видимое.В реальном коде результат, вероятно, будет не таким красивым.

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

Другие советы

Нет, приведения в стиле C могут действовать как reinterpret_casts, const-castы или static_casts в зависимости от ситуации.Вот почему они обескуражены - вы видите приведение в коде в стиле 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

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

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

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

Таким образом, хотя doit отбрасывает const , в этом примере базовая строка не является const, поэтому неопределенного поведения нет.

Обновить

Хорошо, вот лучший пример того, когда использование 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.

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

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

смотрите также

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top