Можно ли полностью избежать бросков с хорошим дизайном? - PullRequest
2 голосов
/ 12 декабря 2011

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

Является ли приведение (будь то стиль C ++, как static_cast или dynamic_cast, или обычное приведение в стиле C) запахом кода? Я вижу, как иногда это полезно, но я думаю, что этого также можно избежать. Нарушает ли кастинг какие-либо правила ООП?

Ответы [ 4 ]

3 голосов
/ 12 декабря 2011

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

Есть "это несовершенно", и есть "OMGWTF, Y U SO DUMB". Слепки просто несовершенны.

2 голосов
/ 12 декабря 2011

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

Для интересного примера (я всегда нахожу стирание типа интересным), посмотритепри реализации boost::any, где dynamic_cast требуется для безопасного чтения из сохраненного значения (в отличие от union s, где вы должны угадать тип и ограничены)

Эскиз:

struct any_base {
   virtual ~any_base() {}
};
template <typename T>
struct any_data : any_base {
   T value;
   any_data( T const & value ) : value(value) {}
};
struct any {
   any_base * data;
   any() : data() {}
   ~any() { delete data; }

   template <typename T>
   any( T const & v ) : data( new any_data<T>(v) {}
}
template <typename T>
T any_cast( any const & a ) {
   any_base<T> * p = dynamic_cast< any_base<T>* >( a.data );
   if ( !p ) throw invalid_cast();
   return *p;
}
0 голосов
/ 12 декабря 2011

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

template <class fn_t, class result_t>
class lazy_t {
    fn_t fn_;
public:

    lazy_t(fn_t fn) : fn_ (fn) { }

    operator result_t () { return fn_(); }
};

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

dynamic_cast <> также необходим в некоторых случаях, таких как реализация множественной диспетчеризации.Подробнее см. http://en.wikipedia.org/wiki/Multiple_dispatch.

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

0 голосов
/ 12 декабря 2011

Типичным примером является I / O: это одна из немногих и единственных причин для приведения, и он также использует один из единственных допустимых типов указателей в C ++: char *:

uint32_t n;
infile.read(reinterpret_cast<char *>(&n), sizeof n);
n *= 2;
outfile.write(reinterpret_cast<const char *>)(&n), sizeof n);

Для других операций, подобных вводу-выводу, требуется аналогичный шаблон, такой как шифрование или преобразование кодировки.

(Другой допустимый указатель в C ++ - void * при использовании в контексте выделения, но его не нужно приводить: способ C ++ «преобразовать» указатель памяти в указатель на объект через конструкцию : void * addr = get_memory();, затем T * p = new (addr) T;.)

...