Я вижу (для меня) странное исключение нарушения прав доступа. Я постараюсь максимально уменьшить проблему. У меня есть класс A и одноэлементный объект sing_. Код выглядит примерно так:
class A {
A();
Sing& sing_;
}
A::A() : sing_(Sing::instance()){
call a method that creates a local copy of Singleton Sing.
....
}
Класс Sing наследуется от Singleton:
class Sing : public Singleton<Sing>{
friend class Singleton<Sing>;
....
}
Сам синглтон выглядит так (это реализация в библиотеке QuantLib)
template <class T>
class Singleton : private boost::noncopyable {
public:
static T& instance();
protected:
Singleton() {}
};template <class T>
T& Singleton<T>::instance() {
static std::map<Integer, boost::shared_ptr<T> > instances_;
#if defined(QL_ENABLE_SESSIONS)
Integer id = sessionId();
#else
Integer id = 0;
#endif
boost::shared_ptr<T>& instance = instances_[id];
if (!instance)
instance = boost::shared_ptr<T>(new T);
return *instance;
}
Мой код проекта встроен в среду Qt Gui. Запуск его в режиме отладки не вызывает проблем. Вещи меняются ужасно, когда я пытаюсь запустить в режиме выпуска. Это основной метод:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GUI w;
w.show();
w.setup(argc, argv);
return a.exec();
}
Наконец, графический интерфейс класса выглядит так:
class GUI : public QMainWindow
{
Q_OBJECT
public:
GUI(QWidget *parent = 0, Qt::WFlags flags = 0);
~GUI();
private:
boost::shared_ptr<A> a_;
};
Когда я запускаю этот код в режиме выпуска, происходит следующее:
Вызывается метод с именем ___ tmainCRTstartup (). В этом метод _WinMain вызывается.
Внутри этого метода WinMain (даже до вызова основного метода и объекта GUI
создается) конструктор A называется. Это означает, что член поет будет инициализирован.
sing_ инициализируется во время вызова Sing :: instance (). Пока все выглядит хорошо.
Конструктор A выполнен. При этом создается локальная ссылка на одноэлементный объект Sing. Вызов Sing :: instance () приводит к нарушению доступа в этой строке
boost::shared_ptr<T>& instance = instances_[id];
Когда я смотрю на instances_ [id] в этом месте (отладка в режиме выпуска), карта выглядит довольно разрушенной. Это означает, что на карте есть один элемент. Но ключ не 0, а очень отрицательное целое число, а также значение выглядит странно.
Я абсолютно не знаю, что здесь не так.
Изменение члена sing_ в A на статический член устраняет проблему:
class A {
A();
static Sing& sing_;
}
Sing& sing_ = Sing::instance();
A::A() {
call a method that creates a local copy of Singleton Sing.
....
}
Это, конечно, хорошо, но мне действительно интересно, в чем «большая» разница между этими двумя реализациями. Почему первый путь заканчивается нарушением прав доступа? Любая подсказка приветствуется.
Я не уверен насчет пути решения проблемы, но могу предложить некоторую помощь в достижении этой цели:
Прежде всего, наиболее распространенные причины, по которым программы, работающие в режиме отладки, терпят неудачу в выпуске (и наоборот):
При отладке вся память обычно (большинство известных мне отладчиков) инициализируется как 0. Поэтому проверки безопасности nullptr позволяют избежать ошибок. В выпуске память не инициализируется, поэтому вы получаете мусор / случайные значения.
Timing. Оптимизация кода и отсутствие отладочных проверок и настроек (например, установка всего 0) делают код быстрее, поэтому существует разная синхронизация между потоками.
Теперь вернемся к вашему делу. Причина, по которой вы видите переменные как «уничтоженные», как вы это выразили, возможно, из-за оптимизации кода.
Обычно, если у вас включена оптимизация кода, компилятор может решить поместить некоторые переменные в аппаратные регистры, а не в стек, как они предназначены.
Это может привести к неправильной интерпретации отладчиком некоторых локальных переменных. (Обычно указатель * this хранится в регистре).
Теперь оператор [] на карте не должен вызывать исключение. Если у карты нет значения для этого ключа, то она будет создана.
Поэтому единственное объяснение, которое я могу придумать, заключается в том, что сама карта повреждена, поэтому, когда она пытается перебрать узлы карты, происходит сбой.
Этот тип повреждения обычно вызывается двумя потоками, пытающимися изменить карту одновременно.
Это возможно в вашем случае, потому что в этой простой одноэлементной реализации нет защиты от блокировки.
Еще один признак того, что это может иметь место, заключается в том, что когда вы делаете его статичным, проблема решается. Создание статической переменной приводит к тому, что инициация объекта происходит намного раньше, что может быть именно тем временем, которое вам необходимо для решения проблемы многопоточности.
Других решений пока нет …