Создание объекта Singleton в списке инициализаторов вызывает нарушение прав доступа (только в режиме выпуска)

Я вижу (для меня) странное исключение нарушения прав доступа. Я постараюсь максимально уменьшить проблему. У меня есть класс 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_;
};

Когда я запускаю этот код в режиме выпуска, происходит следующее:

  1. Вызывается метод с именем ___ tmainCRTstartup (). В этом метод _WinMain вызывается.

  2. Внутри этого метода WinMain (даже до вызова основного метода и объекта GUI
    создается) конструктор A называется. Это означает, что член поет
    будет инициализирован.

  3. sing_ инициализируется во время вызова Sing :: instance (). Пока все выглядит хорошо.

  4. Конструктор 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.
....
}

Это, конечно, хорошо, но мне действительно интересно, в чем «большая» разница между этими двумя реализациями. Почему первый путь заканчивается нарушением прав доступа? Любая подсказка приветствуется.

2

Решение

Я не уверен насчет пути решения проблемы, но могу предложить некоторую помощь в достижении этой цели:

Прежде всего, наиболее распространенные причины, по которым программы, работающие в режиме отладки, терпят неудачу в выпуске (и наоборот):

  1. При отладке вся память обычно (большинство известных мне отладчиков) инициализируется как 0. Поэтому проверки безопасности nullptr позволяют избежать ошибок. В выпуске память не инициализируется, поэтому вы получаете мусор / случайные значения.

  2. Timing. Оптимизация кода и отсутствие отладочных проверок и настроек (например, установка всего 0) делают код быстрее, поэтому существует разная синхронизация между потоками.

Теперь вернемся к вашему делу. Причина, по которой вы видите переменные как «уничтоженные», как вы это выразили, возможно, из-за оптимизации кода.
Обычно, если у вас включена оптимизация кода, компилятор может решить поместить некоторые переменные в аппаратные регистры, а не в стек, как они предназначены.
Это может привести к неправильной интерпретации отладчиком некоторых локальных переменных. (Обычно указатель * this хранится в регистре).

Теперь оператор [] на карте не должен вызывать исключение. Если у карты нет значения для этого ключа, то она будет создана.

Поэтому единственное объяснение, которое я могу придумать, заключается в том, что сама карта повреждена, поэтому, когда она пытается перебрать узлы карты, происходит сбой.

Этот тип повреждения обычно вызывается двумя потоками, пытающимися изменить карту одновременно.
Это возможно в вашем случае, потому что в этой простой одноэлементной реализации нет защиты от блокировки.
Еще один признак того, что это может иметь место, заключается в том, что когда вы делаете его статичным, проблема решается. Создание статической переменной приводит к тому, что инициация объекта происходит намного раньше, что может быть именно тем временем, которое вам необходимо для решения проблемы многопоточности.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]