Программирование >>  Оптимизация возвращаемого значения 

1 ... 7 8 9 [ 10 ] 11 12 13 ... 96


принадлежат к наиболее знающим, и, вероятно, поэтому объект string, включенный ими в библиотеку, не допускает неявного приведения от типа string к char*. Вместо этого существует функция-член c str, выполняющая данное преобразование. Совпадение? Думаю, нет.

С неявным преобразованием, выполняемым конструктором с единственным аргументом, бороться труднее. Более того, проблемы, создаваемые такими конструкторами, зачастую серьезнее, чем те, которые возникают из-за операторов неявного преобразования типа.

В качестве примера рассмотрим шаблон класса для массивов. Эти массивы позволяют определять верхнюю и нижнюю границы индексов:

template<class Т> class Array { public:

Array (int lowBound, inthighBound) ;

Array(intsize);

T&operator[] (intindex);

Первый конструктор позволяет клиенту задавать диапазон индексов массива, например от 10 до 20. Так как у него два аргумента, то эта функция не может быть использована для преобразования типа. Напротив, с помощью второго конструктора допускается создавать объекты Array, указывая только количество элементов в массиве (так же, как это делается для встроенных массивов). Он может быть использован для преобразования типов, и потому является источником постоянного раздражения.

Рассмотрим реализацию сравнения объектов Array<int> и пример обработки таких объектов:

booloperator==(constArray<int>&Ihs,

constArray<int>&rhs); Array<int>a(10); Array<int>b(10);

for (int i=0; i<10; ++i)

if(a==b[i]){ Вместо а

должно быть a [ i] .

/ / Если a [ i ] и b [ i ] равны,

выполнить какие-тодействия.

else {

выполнить другие действия, есш они не равны; }

Мы намеревались сравнить элементы а с соответствующими элементами Ь, но сл5айно пропустили индекс при а. Конечно, хотелось бы, чтобы эта ошибка вызвала у компиляторов поток нелицеприятных комментариев, но они не будут жаловаться, поскольку встретят вызов operator== с аргументами типа



Array<int> (для a) и int (для b). И хотя operator== с аргументами такого типа не определен, компиляторы заметят, что они могут преобразовать int в объект типа Аггау< int >, вызвав конструктор Array<int>, принимающий int в качестве единственного аргумента. Именно это они и сделают, создав код, который явно не предполагался:

for (int i = 0; i<10; + + i)

if (a==static cast<Array<int (b[i] ) ) . . .

Ha каждом шаге цикла программа сравнивает содержимое а с содержимым временного массива размерности b [ i ] (содержимое которого, скорее всего, не определено). Эта программа не только некорректна, но и крайне неэффективна, потому что на каждом шаге создает и удаляет временный объект Array<int> (см. правило 19).

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

Простой вариант состоит в использовании одной из новых черт С++, а именно ключевого слова explicit. Эта особенность была специально введена для решения проблемы неявного преобразования типов, и ее применение предельно облегчено. Если конструктор объявлен с атрибутом explicit, то компиляторам запрещается вызывать его для неявного преобразования типа, в то время как явное преобразование разрешено:

template<class Т> class Array { public:

explicit Array(intsize); Использование explicit.

Array<int>a(10); Нормально, конструктор,

объявленный как explicit, / / может быть использован / / для создания объектов.

Array<int>b(10); Также нормально.

if(a==b[i]) ... Ошибка!Невозможно

неявно преобразовать int / / BArray<int>.

if (a==Array<int>(b[i] ) ) ... Нормально, явное

преобразование типа от int BArray<int>



разрешено (хотя логика / / программы и выглядит подозрительно). if {a==static cast<ArraY<int>>(b[i])) . . .

/ / Также нормально,

/ / логика подозрительна.

if {а== {ArraY<int>)b[i] ) . . .

Преобразования

/ / в стиле С также разрешены,

/ / но программа

продолжает оставаться

подозрительной.

В примере, использующем static cast (см. правило 2), пробел, разделяющий два знака >, неслучаен. Если бы выражение было написано следующим образом:

if {a==static cast<Array<int {b[i]) ) .. .

оно имело бы другой смысл. Это вызвано тем, что компиляторы С++ рассматривают выражение как единую конструкцию. Без пробела между символами > выражение породило бы синтаксическую ошибку.

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

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

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

Снова рассмотрим шаблон Array. Допустим, требуется разработать способ, который бы разрешал передавать конструктору целочисленный размер массива в качестве аргумента и в то же время запрещал неявное преобразование целых чисел во временный объект Array. Сначала создается новый класс ArraySize. Объекты этого типа служат единственной цели: они описывают размерность массива, который будет создан. Затем конструктор с одним аргументом Array переписывается так, чтобы принимать в качестве аргумента ArraySize, а не int. Исходный текст выглядит следующим образом:

template<class Т> class Array { public:

class ArraySize{ Новыйкласс.



1 ... 7 8 9 [ 10 ] 11 12 13 ... 96

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