Question

I would like to keep track of thisClass (inherits QObject) instances ("siblings") by appending this (the pointer to self) to a "shared" static QList class member:

private:
  static QList<thisClass*> _plist;

However, the static member declaration causes an LNK2001 unresolved external symbol. Without static, the program builds (but each object only has its own data).

What might cause the error, is there a fix, and how should "keeping track of class instances" be done properly?

Was it helpful?

Solution

You have declared static member. Now you need to define it in cpp file:

QList<thisClass*> thisClass::_plist;

OTHER TIPS

What you have done is only a declaration, you also need to define the member variable. This is done in one of your source files.

If you don't need random access iteration of the list, you could also use an intrusive container to allow iteration of the siblings: it'd have lower overhead, as the list nodes are stored in the objects themselves. The list can be iterated just like std::list, and it tracks the dynamic lifetime of the objects - it's not unlike like a QPointer, except that it's a list.

// myclass.h - interface
#include <QObject>
#include <boost/intrusive/list.hpp>
class MyClass : public QObject, private boost::intrusive::list_base_hook<> {
  using list_t = boost::intrusive::list<MyClass>;
  static list_t m_siblings;
  friend list_t;
  static QThread const *listThread() {
    return m_siblings.empty() ? QThread::currentThread() : m_siblings.front().thread();
  }
protected:
  bool event(QEvent * ev) override {
    if (ev->type() == QEvent::ThreadChange)
      Q_ASSERT(m_siblings.size() <= 1);
    return QObject::event(ev);
  }
public:
  MyClass(QObject *parent = {}) : QObject{parent} {
    Q_ASSERT(listThread() == QThread::current_thread());
    m_siblings.push_back(*this);
    qDebug() << "there are" << m_siblings.size() << "objects in existence";
  }
  ~MyClass() override {
    m_list.erase(m_siblings.iterator_to(*this));
  }
};

// myclass.cpp - implementation
#include "myclass.h"
boost::intrusive::list<MyClass> MyClass::m_siblings;

It is enforced that all siblings are in the same thread; this is necessary for thread-safe access of the list. If the objects were to live in arbitrary threads, the list access needs to be protected by a mutex:

// myclass.h - interface
#include <QObject>
#include <boost/intrusive/list.hpp>
class MyClass : public QObject, private boost::intrusive::list_base_hook<> {
  using list_t = boost::intrusive::list<MyClass>;
  static QReadWriteLock m_siblingsMutex;
  static list_t m_siblings;
  friend list_t;
public:
  MyClass(QObject *parent = {}) : QObject{parent} {
    QWriteLocker lock(&m_siblingsMutex);
    m_siblings.push_back(*this);
  }
  ~MyClass() override {
    QWriteLocker lock(&m_siblingsMutex);
    m_siblings.erase(m_siblings.iterator_to(*this));
  }
  void dumpSiblings() {
    QReadLocker lock(&m_siblingsMutex);
    for (auto const &obj : m_siblings)
       qDebug() << "MyClass at " << &obj;
};

// myclass.cpp - implementation
#include "myclass.h"
QReadWriteLock MyClass::m_siblingsMutex;
boost::intrusive::list<MyClass> MyClass::m_siblings;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top