Стандарты программирования на С++. 101 правило и рекомендация | страница 71
Пример 1. Перегрузка. Пусть у нас есть, например, >Widget::Widget(unsigned int)
, который может быть вызван неявно, и функция >Display
, перегруженная для >Widget
и >double
. Рассмотрим следующий сюрприз при разрешении перегрузки:
>void Display(double); // вывод double
>void Display(const Widget&); // Вывод Widget
>Display(5); // гм! Создание и вывод Widget
Пример 2. Работающие ошибки. Допустим, вы снабдили класс >String
оператором operator >const char*
:
>class String {
> // ...
>public:
> operator const char*(); // Грустное решение...
>};
В результате этого становятся компилируемыми масса глупостей и опечаток. Пусть >s1
и >s2
— объекты типа >String
. Все приведенные ниже строки компилируются:
>int x = s1 - s2; // Неопределенное поведение
>const char* р = s1 - 5; // Неопределенное поведение
>р = s1 + '0'; // делает не то, что вы ожидаете
>if (s1 == "0") { ... } // делает не то, что вы ожидаете
Именно по этой причине в стандартном классе >string
отсутствует >operator const char*
.
При нечастом и осторожном использовании неявные преобразования типов могут сделать код более коротким и интуитивно более понятным. Стандартный класс >std::string
определяет неявный конструктор, который получает один аргумент типа >const char*
. Такое решение отлично работает, поскольку проектировщики класса приняли определенные меры предосторожности.
• Не имеется автоматического преобразования >std::string
в >const char*
; такое преобразование типов выполняются при помощи двух именованных функций — >c_str
и >data
.
• Все операторы сравнений, определенные для >std::string
(например, >==
, >!=
, ><
), перегружены для сравнения >const char*
и >std::string
в любом порядке (см. рекомендацию 29). Это позволяет избежать создания скрытых временных переменных.
Но и при этом возникают определенные неприятности, связанные с перегрузкой функций.
>void Display(int);
>void Display(std::string);
>Display(NULL); // вызов Display(int)
Этот результат для некоторых может оказаться сюрпризом. (Кстати, если бы выполнялся вызов >Display(std::string)
, код бы обладал неопределенным поведением, поскольку создание >std::string
из нулевого указателя некорректно, но конструктор этого класса не обязан проверять передаваемое ему значение на равенство нулю.)
[Dewhurst03] §36-37 • [Lakos96] §9.3.1 • [Meyers96] §5 • [Murray93] §2.4 • [Sutter00] §6, §20, §39
41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур С)