Избежать подобной проблемы просто: используйте отдельные предложения для создания объекта Widget и помещения его в интеллектуальный указатель, а затем передайте этот интеллектуальный указатель processWidgets:
std::tr1::shared_ptr pw(new Widget); // поместить новый объект
// в интеллектуальный указатель
// в отдельном предложении
processWidget(pw, priority()); // этот вызов не приведет
// к утечке
Такой способ работает потому, что компиляторам предоставляется меньше свободы в переопределении порядка операций в разных предложениях, чем в одном. В модифицированном коде выражение «new Widget» и вызов конструктора tr1::shared_ptr отделены от вызова priority, поэтому компилятор не может вставить вызов priority между ними.
Что следует помнить
• Помещайте объекты, выделенные оператором new, в «интеллектуальные» указатели в отдельном предложении. В противном случае такие вызовы могут привести к утечкам ресурсов, если возникнет исключение.
Глава 4
Проектирование программ и объявления
Проектирование программного обеспечения – это приемы получения программ, которые делают то, чего вы от них хотите. Обычно проект начинается с довольно общей идеи, но затем обрастает деталями настолько, чтобы можно было приступить к разработке конкретных интерфейсов. Интерфейсы должны затем превратиться в объявления на языке C++. В настоящей главе мы рассмотрим проблему проектирования и объявления хороших интерфейсов на C++. Начнем с одного из самых важных правил проектирования интерфейсов: использовать их правильно должно быть просто, а неправильно – трудно. Отталкиваясь от этой мысли, мы сформулируем ряд более конкретных правил, касающихся самых разных тем, а именно: корректность, эффективность, инкапсуляция, удобство сопровождения, расширяемость и следование принятым соглашениям.
Представленный в этой главе материал не охватывает всего, что нужно знать о проектировании хороших интерфейсов. Мы остановимся лишь на некоторых из наиболее важных соглашений, укажем на наиболее типичные ошибки и предложим решения проблем, часто возникающих перед проектировщиками классов, функций и шаблонов.
Правило 18: Проектируйте интерфейсы так, что их легко было использовать правильно и трудно – неправильно
C++ изобилует интерфейсами. Интерфейсы функций. Интерфейсы классов. Интерфейсы шаблонов. Каждый интерфейс – это средство, посредством которого пользователь взаимодействует с вашим кодом. Предположим, что вы имеете дело с разумными людьми, которые стремятся хорошо сделать свою работу. Они хотят применять ваши интерфейсы корректно. Если случится, что они применят какой-то из них неправильно, то часть вины за это ляжет на вас. В идеале, при попытке использовать интерфейс так, что пользователь не получит ожидаемого результата, код не должен компилироваться. А если компилируется, то должен делать то, что имел в виду пользователь.
При разработке интерфейсов, простых для правильного применения и трудных – для неправильного, вы должны предвидеть, какие ошибки может допустить пользователь. Например, предположим, что вы разрабатываете конструктор класса, представляющего дату:
class Date {
public:
Date(int month, int day, int year);
...
};
На первый взгляд, этот интерфейс может показаться разумным (во всяком случае, в США), но есть, по крайней мере, две ошибки, которые легко может допустить пользователь. Во-первых, он может передать параметры в неправильном порядке:
Date(30, 3, 1995); // должно быть “3, 30”, а не “30, 3”
Во-вторых, номер месяца или дня может быть указан неверно:
Date(2, 20, 1995); // Должно быть “3, 30”, а не “2, 20”
(Последний пример может показаться надуманным, но вспомните, что на клавиатуре «2» находится рядом с «3». Такие опечатки случаются сплошь и рядом.)
Многих ошибок можно избежать за счет введения новых типов. Система контроля типов – ваш первый союзник в деле предотвращения компилируемости нежелательного кода. В данном случае мы можем ввести простые типы-обертки, чтобы различать дни, месяцы и годы, затем использовать их в конструкторе Date:
struct Day { struct Month { struct Year {
explicit Day(int d) explicit Month(int m) explicit Year(int y)
: val(d) {} : val(m) {} : val(y) {}
int val; int val; int val;
}; }; };
class Date {
public:
Date(const Month& m, const Day& d, const Year& y(;
...
};
Date d(30, 3, 1995); // ошибка! неправильные типы
Читать дальше
Конец ознакомительного отрывка
Купить книгу