Читаем Thinking In C++. Volume 2: Practical Programming полностью

Any static member object inside a class is an expression of Singleton: one and only one will be made. So in a sense, the language has direct support for the idea; we certainly use it on a regular basis. However, a problem is associated with static objects (member or not), and that’s the order of initialization, as described in Volume 1 of this book. If one static object depends on another, it’s important that the objects are initialized in the correct order.

In Volume 1, you were shown how to control initialization order by defining a static object inside a function. This delays the initialization of the object until the first time the function is called. If the function returns a reference to the static object, it gives you the effect of a Singleton while removing much of the worry of static initialization. For example, suppose you want to create a log file upon the first call to a function that returns a reference to that log file. This header file will do the trick:

//: C10:LogFile.h

#ifndef LOGFILE_H

#define LOGFILE_H

#include

std::ofstream& logfile();

#endif // LOGFILE_H ///:~

The implementation must not be inlined, because that would mean that the whole function, including the static object definition within, could be duplicated in any translation unit where it’s included, which violates C++’s one-definition rule.[117] This would most certainly foil the attempts to control the order of initialization (but potentially in a subtle and hard-to-detect fashion). So the implementation must be separate:

//: C10:LogFile.cpp {O}

#include "LogFile.h"

std::ofstream& logfile() {

  static std::ofstream log("Logfile.log");

  return log;

} ///:~

Now the log object will not be initialized until the first time logfile( ) is called. So if you create a function:

//: C10:UseLog1.h

#ifndef USELOG1_H

#define USELOG1_H

void f();

#endif // USELOG1_H ///:~

that uses logfile() in its implementation:

//: C10:UseLog1.cpp {O}

#include "UseLog1.h"

#include "LogFile.h"

void f() {

  logfile() << __FILE__ << std::endl;

} ///:~

And you use logfile() again in another file:

//: C10:UseLog2.cpp

//{L} LogFile UseLog1

#include "UseLog1.h"

#include "LogFile.h"

using namespace std;

void g() {

  logfile() << __FILE__ << endl;

}

int main() {

  f();

  g();

} ///:~

the log object doesn’t get created until the first call to f( ).

You can easily combine the creation of the static object inside a member function with the Singleton class. SingletonPattern.cpp can be modified to use this approach:[118] 

//: C10:SingletonPattern2.cpp

// Meyers’ Singleton

#include

using namespace std;

class Singleton {

  int i;

  Singleton(int x) : i(x) { }

  void operator=(Singleton&);

  Singleton(const Singleton&);

public:

  static Singleton& instance() {

    static Singleton s(47);

    return s;

  }

  int getValue() { return i; }

  void setValue(int x) { i = x; }

};

int main() {

  Singleton& s = Singleton::instance();

  cout << s.getValue() << endl;

  Singleton& s2 = Singleton::instance();

  s2.setValue(9);

  cout << s.getValue() << endl;

} ///:~

An especially interesting case occurs if two Singletons depend on each other, like this:

//: C10:FunctionStaticSingleton.cpp

class Singleton1 {

  Singleton1() {}

public:

  static Singleton1& ref() {

    static Singleton1 single;

    return single;

  }

};

class Singleton2 {

  Singleton1& s1;

  Singleton2(Singleton1& s) : s1(s) {}

public:

  static Singleton2& ref() {

    static Singleton2 single(Singleton1::ref());

    return single;

  }

  Singleton1& f() { return s1; }

};

int main() {

  Singleton1& s1 = Singleton2::ref().f();

} ///:~

When Singleton2::ref( ) is called, it causes its sole Singleton2 object to be created. In the process of this creation, Singleton1::ref( ) is called, and that causes the sole Singleton1 object to be created. Because this technique doesn’t rely on the order of linking or loading, the programmer has much better control over initialization, leading to fewer problems.

Yet another variation on Singleton allows you to separate the "Singleton-ness" of an object from its implementation. This is achieved through templates, using the Curiously Recurring Template Pattern mentioned in Chapter 5.

//: C10:CuriousSingleton.cpp

// Separates a class from its Singleton-ness (almost)

#include

using namespace std;

template

class Singleton {

public:

  static T& instance() {

    static T theInstance;

    return theInstance;

  }

protected:

  Singleton() {}

  virtual ~Singleton() {}

private:

  Singleton(const Singleton&);

  Singleton& operator=(const Singleton&);

};

// A sample class to be made into a Singleton

class MyClass : public Singleton {

  int x;

protected:

  friend class Singleton;

  MyClass(){x = 0;}

public:

  void setValue(int n) { x = n; }

  int getValue() const { return x; }

};

int main() {

  MyClass& m = MyClass::instance();

  cout << m.getValue() << endl;

  m.setValue(1);

  cout << m.getValue() << endl;

} ///:~

MyClass is made a Singleton by:

1.       Making its constructor private or protected.

2.      Making Singleton a friend.

3.      Deriving MyClass from Singleton.

Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT