简体   繁体   English

如何修复静态类成员的初始化顺序?

[英]How to fix initialisation order of static class member?

Whether the following code throws a SIGSEGV or works as expected depends on the order in which object files appear in the makefile (in the .pro in my case).以下代码是抛出 SIGSEGV 还是按预期工作取决于目标文件在 makefile 中出现的顺序(在我的情况下为 .pro)。 I'm not very confident with just having to maintain them in the correct order.我对只需要以正确的顺序维护它们不是很有信心。 Is there a simple change to my sources, which eliminates the dependency on the correct order of object files?是否对我的源进行了简单的更改,从而消除了对目标文件正确顺序的依赖?

These are the source files, boiled down to a short example.这些是源文件,归结为一个简短的示例。

dummy0.h : dummy0.h :

#ifndef DUMMY0_H
#define DUMMY0_H

#include <QObject>

class Dummy0 : public QObject
{
    Q_OBJECT
public:
    explicit Dummy0(QObject *parent = 0);
};

#endif // DUMMY0_H

dummy0.cpp : dummy0.cpp :

#include "dummy0.h"
#include "unittestclass.h"

Dummy0::Dummy0(QObject *parent) : QObject(parent) {}

ADD_TEST( Dummy0 )

As the name suggests, in the future there will also be Dummy1 ... Dummy<n> .顾名思义,未来还会有Dummy1 ... Dummy<n>

unittestclass.h : unittestclass.h :

#ifndef UNITTESTCLASS_H
#define UNITTESTCLASS_H

#include <QObject>
#include <QTest>
#include <QDebug>
#include <memory>
#include <list>

// see https://stackoverflow.com/questions/12194256/qt-how-to-organize-unit-test-with-more-than-one-class

namespace TestCollector {

  class BaseClass {  // see https://hackernoon.com/shared-static-variable-for-all-template-class-instances-eaed385f332b
  public:
      BaseClass() = default;
      int listClasses( int argc, char *argv[] );
  protected:
      static std::list<std::shared_ptr<QObject>> testlist;
  };

  template <class T>
  class UnitTestClass : public BaseClass {

  public:
      UnitTestClass() {
          std::shared_ptr<QObject> ptr = std::make_shared<T>();
          qDebug() << "pushing a" << ptr.get()->metaObject()->className();
          qDebug() << "testlist size before:" << testlist.size() << "of" << testlist.max_size();
          testlist.size();  // no SIGSEGV !?
          testlist.push_back( ptr );   // SIGSEGV here when race condition
          qDebug() << "testlist size after:" << testlist.size();
      }
  };
}  // TestCollector

#define ADD_TEST(className) static TestCollector::UnitTestClass<className> test;

#endif // UNITTESTCLASS_H

unittestclass.cpp : unittestclass.cpp

#include "unittestclass.h"

namespace TestCollector {

std::list<std::shared_ptr<QObject>> BaseClass::testlist;

int BaseClass::listClasses( int argc, char *argv[] ) {
    int result=0;

    for( const auto c : testlist ) {
        qDebug() << "got" << c.get()->metaObject()->className();
    }
    return result;
}

}  // TestCollector

main.cpp : main.cpp :

#include "unittestclass.h"

int main(int argc, char *argv[]) {
    TestCollector::BaseClass base;
    return base.listClasses( argc, argv );
}

When I have dummy0.cpp before unittestclass.cpp the programme fails at testlist.push_back(ptr) [NB: interestingly enough, the testlist.size() in the line before push_back won't fail].当我在unittestclass.cpp之前有dummy0.cpp ,程序在testlist.push_back(ptr)处失败 [注意:有趣的是,在push_back之前的行中的testlist.size()不会失败]。 When dummy0.cpp appears later, everthing is fine.dummy0.cpp稍后出现时,一切都很好。 This hints to a problem that testlist might be used before it is initialised, when the order of object files is unfavourable!?这暗示了一个问题,当目标文件的顺序不利时,可能会在初始化之前使用 testlist!?

How can I avoid depending on this order?我怎样才能避免依赖这个顺序?

[I currently use c++11, QtCreator 3.5.1, g++ 5.4.0, GNU ld 2.26.1.] [我目前使用 c++11、QtCreator 3.5.1、g++ 5.4.0、GNU ld 2.26.1。]

You can ensure that the object you're depending on exists by indirecting through a function:您可以通过函数间接确保您所依赖的对象存在:

class BaseClass
{
// ...
    // This returns a pointer only to make it hard to copy the list by mistake.
    // Some people prefer to use a reference, and some prefer dynamic allocation.
    // The important part is that the object is created the first time you use it.
    static std::list<std::shared_ptr<QObject>>* get_testlist()
    {
        static std::list<std::shared_ptr<QObject>> tests;
        return &tests;
    } 
};

and then use it something like this:然后像这样使用它:

UnitTestClass() {
      std::shared_ptr<QObject> ptr = std::make_shared<T>();
      qDebug() << "pushing a" << ptr.get()->metaObject()->className();
      auto testlist = get_testlist();
      qDebug() << "testlist size before:" << testlist->size() << "of" << testlist->max_size();
      testlist->push_back( ptr );
      qDebug() << "testlist size after:" << testlist->size();
  } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM