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

1 ... 75 76 77 [ 78 ] 79 80 81 ... 144


Кенига было принято простое решение: копирование объектов определено как почленное копирование не-статических членов и объектов базовых классов.

Согласно этому определению, х=у означает то же, что х. operator= (у). Но здесь есть одно интересное (хотя и не всегда желательное) следствие. Рассмотрим при.мер:

class X { /* ... */ };

class Y : public X {/*...*/ };

void g(X X, Y y) {

X = y; x.operator=(y): правильно

у = X; y.operator=(x): ошибка x - не объект класса Y

По умолчанию присваивание объекту класса X - это X: : operator= (const Х&), поэтому х=у - законная операция, ибо Y открыто наследует X. Обычно такую операцию называют вырезанием (slicing), так как х присваивается срез у. Копирующие конструкторы трактуются аналогично.

С практическо!! точки зрения вырезание представляется мне сомнительным трюком, но я не вижу способа запретить его, не вводя слишком уж специальных правил. Кроме того, в то время Рави Сетхи как раз просил меня ввести именно такую семантику вырезания , она была нужна ему для теоретических и педагогических целей: если не разрешить присваивание объекта производного класса объекту его открытого базового класса, то это будет единственны.м место.м в С++, где производный объект нельзя использовать вместо базового.

Но при этом остается открытой проблема, связанная с операция.ми копирования по умолчанию: указатели-члены копируются, а объекты указания - нет. Это почти всегда неправильно, но запретить операцию нельзя из-за совместимости с С. Однако компилятор легко может выдать предупреждение, если класс с указателем-членом инициализируется при помощи копирующего конструктора или оператора присваивания по умолчанию. Например:

class String {

char *р;

int sz; public:

здесь не определены операции копирования (небрежность)

void f(const Strings s) {

String s2 = s; предупреждение: копируется указатель s2 = s; предупреждение: копируется указатель

Се.мантику присваивания и копирования по умолчанию можно назвать поверхностным копированием. Иными словами, копируются члены класса, но не объекты, на которые эти члены указывают. Альтернативу - рекурсивное копирование



указываемых объектов (так называемое глубокое копирование) - следует определять явно. Поскольку объекты могут указывать сами на себя, то вряд ли возможно другое решение. В обычном случае не стоит пытаться определять присваивание как глубокое копирование; гораздо лучше определить виртуальную (или невиртуальную) функцию копирования (см. [2nd, стр. 217-220] и раздел 13.7).

11.5. Удобство нотации

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

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

11.5.1. Умные указатели

До версии 2.0 пользователи не могли переопределять оператор разыменования ->. Это осложняло создание классов объектов, ведущих себя как умные указатели. При определении перегрузки операторов -> виделся мне как бинарный оператор со специальными правилами для правого операнда (имени члена). В этой связи вспоминается встреча в компании Mentor Graphics в Орегоне, когда. Джим Ховард (Jim Howard) доказал мне, что я заблуждался. Оператор ->, объяснил он, можно рассматривать как унарный постфиксный оператор, результат которого применяется к имени члена. Пересматривая механизм перегрузки, я воспользовался этой идеей.

Если тип значения, возвращаемого функцией operator-> (), используется, то оп должен быть указателем на класс или объект класса, в котором определен operator-> {). Например:

struct Y { int m; };

class Ptr {

Y* p;

. . . public:

Ptr(Symbolic ref);

-Ptr;

Y* operator->() {

проверить p return p;



Здесь класс Ptr определен так, что его объекты ведут себя как указатели на объекты класса Y с тем отличием, что при каждом обращении производятся некоторые вычисления:

void f(Ptr X, Ptr& xr, Ptr* xp) {

x->m; x.operator->()->m; то есть x.p->m xr->m; xr.operator->()->m; то есть xr.p->n xp->m; ошибка: у Ptr нет члена m

Такие классы особенно полезны в виде шаблонов (см. раздел 15.9.1), [2nd]:

template<class Y> class Ptr { /* ... */ };

void f(Ptr<complex> pc, Ptr<Shape> ps) { /* ... */ }

Это было понятно уже после реализации перегрузки -> в 1986 г. К сожалению, написать такой код удалось лишь после реализации шаблонов.

Для обычных указателей - > - это синоним некоторых применений * и []. Например, для объявления У* р имеют место тождества:

р->т == (*р).т ==.р[0].т

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

class Ptr {

Y* р; public:

Y* operator->() { return p; }

Y&; operator* 0 { return *p; }

Y& operator[](int i) { return p[i]; }

...

Перегрузка оператора -> важна для целого класса профа,мм. Причина в том, что косвенное обращение - ключевая концепция, а перегрузка -> дает красивый, прямой и эффективный способ реализации ее в програ.ммах. Еше одно применение оператора -> - это ограниченная, но очень полезная фор.ма реализации делегирования в С++ (см. раздел 12.7).

11.5.2. Умные ссылки

Решив разрешить перегрузку оператора ->, я задумался над те.м, можно ли аналогичным образом перегрузить оператор . (точка).

В то время мне казались убедительными следующие рассуждения: если obj -это объект класса, то obj .m имеет смысл для каждого члена m этого объекта. Переопределяя встроенные операции, мы не хотим получить мутирующий язык (хотя по очень вески.м причинам это правило нарушено для =, а также для унарного &).



1 ... 75 76 77 [ 78 ] 79 80 81 ... 144

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