Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 105 106 107 [ 108 ] 109 110 111 ... 144


D* pdl = dynamic cast<D*>(pb); D* pd2 = static cast<D*>(pb);

Если в действительности pb указывает на D, то pdl и pd2 получают одно и то же значение. Так же, как и в случае, когда рЬ==0. Но если pb указывает на В (и только), то у dynamic cast достаточно информации, чтобы вернуть О, а static cast должен поверить програ.ммисту, что pb указывает на D, и вернуть указатель на объект, предположительно принадлежащий классу D. Рассмотрим другой пример:

class Dl : public D {/*...*/ }; class D2 : public В { /* ... */ }; class X : public Dl, public D2 {/*... */ };

void g() {

D2* pd2 = new X; f(pd2);

Здесь g () вызовет f {), передав объект в, который не является подобъектом D. Следовательно, dynamic cast правильно найдет соседний подобъект типа D, тогда как static cast вернет указатель на какой-то неподходящий подобъект X. Насколько я помню, первым, кто обратил мое внимание на этот феномен, был Мартин ОРиордан.

14.3.3. Оператор reinterpret cast

Нотация reinterpret cast<T> (е) призвана заменить (Т) е для преобразований вида char* в int* и Some class* BUnrelated class*, которые внутренне небезопасны и зачастую зависят от реализации. При этом Some сlass и Unrelated class не связаны друг с другом отношением наследования. По сути дела, оператор reinterpret cast возвращает значение, получеппое в результате новой принудительной интерпретации своего аргумента. Например:

class S; class Т;

void f(int* pi, cliar* pc, S* ps, T* pt, int i) (

S* ps2 = reinterpret cast<S*>(pi); S* ps3 = reinterpret cast<S*>(pt); char* pc2 = reinterpret cast<char*>(pt); int* pi2 = reinterpret cast<int*>(pc); int 12 = reinterpret cast<int>(pc); int* pi3 = reinterpret cast<int*>(i);

Оператор reinterpret cast позволяет преобразовать указатель в любой другой, а также любой интегральный тип в любой указательный тип и наоборот.



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

В отличие от static cast, на результаты работы reinterpret cast нельзя полагаться ни в одном из случаев за исключением приведения к исходному типу. Все остальные применения в лучшем случае непереносимы. Вот почему преобразования между указателем на функцию и указателем на член относятся к reinterpret cast, а не к static cast. Например:

void thump(char* р) { *р = х; }

typedef void (*PF)(const char*); PF pf;

void g(const char* pc) {

thump(pc); ошибка: неправильный тип аргумента pf = &thump; ошибка

pf = static cast<PF>(bthump); ошибка!

pf = reinterpret cast<PF> (Scthump) ; допускается,

HO за последствия отвечаете только вы

pf(pc); правильная работа не гарантируется!

Понятно, что присваивать pf указатель на thump опасно, так как это действие в обход системы типов. С помощью данной операции адрес константы может быть передан какой-то функции, которая сделает попытку ее модифицировать. Поэтому и нужно использовать приведение, причем именно оператор reinterpret cast. Но для многих неожиданно, что вызов thump через pf все равно не гарантирован (в С-и- так же, как и в С). Дело в том, что компилятору разрешено использовать разные соглашения о вызове для функций разных типов. В частности, есть веские основания для того, чтобы константные и неконстантные функции вызывались по-разному.

Заметим, что reinterpret cast не осуществляет навигацию по иерархии классов. Например:

class А {/*...*/ }; class В {/*...*/ };

class D : public А, public В {/*...*/ };

void f(B* pb) {

D* pdl = reinterpret cast<D*>(pb); D* pd2 = static cast<D*>(pb);



Здесь pdl и pd2, как правило, получат различные значения. При вызове f(new D);

pd2 будет указывать на начало переданного объекта D, тогда как pdl - на начало подобъекта В в объекте D.

Операция reinterpret cast <Т> {arg) почти так же неудачна, как и (Т) arg. Однако она более заметна в тексте программы, никогда не осуществляет навигации по классам и не отбрасывает атрибут const. Для данной операции есть альтернативы, reinterpret cast - средство для низкоуровневых преобразований, обычно зависящих от реализации, - и только!

14.3.4. Оператор const cast

Труднее всего было найти замену для старых приведений типов, чтобы корректно обрабатывался атрибут const. В идеале нужно было гарантировать, что константность никогда не снимается сама собой. По этой причине каждый из операторов reinterpret cast, dynamic cast и static cast не изменял этот атрибут.

Нотация const cast<T> (е) призвана заменить (Т) е для преобразований, которым нужно получить доступ к данны.м с модификатором const или volatile. Например:

extern С char* strchr(char*, char);

inline const char* strchr(const char* p, char c) {

return strchr(const cast<char*>p, char c);

В const cast<T> (e) тип аргумента Т должен совпадать с типом аргу.мента е во всем, кроме модификаторов const и volatile. Результато.м будет то же значение, что у е, только его типом станет т.

Отметим, что результат отбрасывания const для объекта, который первоначально был объявлен с этим модификатором, не определен (см. раздел 13.3).

14.3.4.1. Проблемы защиты const

В системе типов есть некоторые тонкости, которые открывают дыры в защите от неявного нарушения константности. Рассмотрим пример:

const char сс = а; const char* pec = &сс; const char** ppcc = &pcc;

void* pv = ppcc; никакого приведения не нужно:

ppcc - не константный указатель, он лишь указывает на таковой, но константность исчезла!

char** ррс - (char**)pv; указывает на рсс

void f() {

**ррс = х; теперь можем изменять константную переменную!



1 ... 105 106 107 [ 108 ] 109 110 111 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика