Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - [17]
Рассмотрим пример. Предположим, у вас есть класс FileSystem, который делает файлы из Internet неотличимыми от локальных. Поскольку ваш класс представляет мир как единую файловую систему, вы могли бы создать в глобальной области действия или в пространстве имен соответствующий ей специальный объект:
>class FileSystem { // из вашей библиотеки
>public:
>...
>std::size_t numDisks() const; // одна из многих функций-членов
>...
>};
>extern FileSystem tfs; // объект для использования клиентами
>// “tfs” = “the file system”
Класс FileSystem определенно не тривиален, поэтому использование объекта theFileSystem до того, как он будет сконструирован, приведет к катастрофическим последствиям.
Теперь предположим, что некий пользователь создает класс, описывающий каталоги файловой системы. Естественно, его класс будет использовать объект theFileSystem:
>class Directory { // создан пользователем
>public:
>Directory( params );
>...
>};
>Directory::Directory( params )
>{
>...
>std::size_t disks = tfs.numDisks(); // использование объекта tfs
>...
>}
Далее предположим, что пользователь решает создать отдельный глобальный объект класса Directory, представляющий каталог для временных файлов:
>Directory tempDir( params ); // каталог для временных файлов
Теперь проблема порядка инициализации становится очевидной: если объект tfs не инициализирован раньше, чем tempDir, то конструктор tempDir попытается использовать tfs до его инициализации. Но tfs и tempDir были созданы разными людьми в разное время и находятся в разных исходных файлах – это нелокальные статические объекты, определенные в разных единицах трансляции. Как вы можете быть уверены, что tfs будет инициализирован раньше, чем tempDir?
Да никак! Еще раз повторю: относительный порядок инициализации нестатических локальных объектов, определенных в разных единицах трансляции, не определен. На то есть своя причина. Определить «правильный» порядок инициализации нелокальных статических объектов трудно. Очень трудно. Неразрешимо трудно. В наиболее общем случае – при наличии многих единиц трансляции и нелокальных статических объектов, сгенерированных путем неявной конкретизации шаблонов (которые и сами могут быть результатом неявной конкретизации других шаблонов) – не только невозможно определить правильный порядок инициализации, но обычно даже не стоит искать частные случаи, когда этот порядок в принципе определить можно.
К счастью, небольшое изменение в проекте программы позволяет полностью устранить эту проблему. Нужно лишь переместить каждый нелокальный статический объект в отдельную функцию, в которой он будет объявлен статическим. Эти функции возвращают ссылки на объекты, которые в них содержатся. Клиенты затем вызывают функции вместо непосредственного обращения к объектам. Другими словами, нелокальные статические объекты заменяются локальными статическими объектами (знакомые с паттернами проектирования легко узнают в этом описании типичную реализацию паттерна Singleton).
Этот подход основан на том, что C++ гарантирует: локальные статические объекты инициализируются в первый раз, когда определение объекта встречается при вызове этой функции. Поэтому если вы замените прямой доступ к нелокальным статическим объектам вызовом функций, возвращающих ссылки на расположенные внутри них локальные статические объекты, то можете быть уверены, что ссылки, возвращаемые из функций, будут ссылаться на инициализированные объекты. Дополнительное преимущество заключается в том, что если вы никогда не вызываете функцию, эмулирующую нелокальный статический объект, то и не придется платить за создание и уничтожение объекта, чего не скажешь о реальных нелокальных статических объектах.
Вот как этот прием применяется к объектам tfs и tempDir:
>class FileSystem {...}; // как раньше
>FileSystem& tfs() // эта функция заменяет объект tfs, она может
>{ // быть статической в классе FileSystem
>static FileSystem fs; // определение и инициализация локального
>// статического объекта
>return fs; // возврат ссылки на него
>}
>class Directory {...}; // как раньше
>Directory::Directory( params ) // как раньше, но вместо ссылки на tfs
>{ // вызов tfs()
>...
>std::size_t disks = tfs().numDisks();
>...
>}
>Directory& tempDir() // эта функция заменяет объект tempDir,
>{ // может быть статической в классе Directory
>static Directory td; // определение/инициализация локального
>// статического объекта
>return td; // возврат ссылки на него
>}
Клиенты работают с этой модифицированной программой так же, как раньше, за исключением того, что вместо tfs и tempDir они теперь обращаются к tfs() и tempDir(). Иными словами, используют ссылки на объекты, возвращенные функциями, вместо использования самих объектов.
Функции, которые в соответствии с данной схемой возвращают ссылки, всегда просты: определить и инициализировать локальный статический объект в строке 1 и вернуть его в строке 2. В связи с этим у вас может возникнуть искушение объявить их встроенными, особенно, если они часто вызываются (см. правило 30). С другой стороны, тот факт, что эти функции содержат в себе статические объекты, усложняет их применение в многопоточных системах. Но тут никуда не деться: неконстантные статические объекты любого рода – локальные или нелокальные – представляют проблему в случае наличия в программе нескольких потоков. Решить ее можно, например, вызвав самостоятельно все функции, возвращающие ссылки, на этапе запуска программы, когда еще работает только один поток. Это исключит неопределенность в ходе инициализации.
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.