Skip to content

Instantly share code, notes, and snippets.

@biochimia
Created August 26, 2012 13:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biochimia/3479321 to your computer and use it in GitHub Desktop.
Save biochimia/3479321 to your computer and use it in GitHub Desktop.
This about sums up everything that is wrong with QObject.
struct QObject;
struct QObjectPrivate
{
virtual ~QObjectPrivate();
QObject *q;
void *data[14];
};
struct QObject
{
virtual ~QObject();
private:
// non-copyable
QObject(const QObject &);
QObject &operator=(const QObject &);
QObjectPrivate *d;
};
struct Object
: QObject
{
// ...
};
void foo()
{
Object *obj = new Object;
// ...
// obj eventually gets deleted.
}
@tsondergaard
Copy link

I don't get it

@biochimia
Copy link
Author

@tsondergaard, consider this:

QObject is essentially a (non-copyable) pointer with a virtual table; the pointee of this pointer, an instance of QObjectPrivate, itself has a virtual table. [QObjectPrivate is also too big to be the base of all classes, but that's a distraction at this point ;-]

The recommended/typical usage involves allocating instances of QObject on the heap -- though this is not mandatory. Either way, most non-trivial use cases will require the use of pointers, for instance, to keep track of parent-child relationships, and for participating in signal-slot connections.

There are a couple of issues I see there.

Virtual tables allow you to extend/modify behavior. For a single class derived from QObject one virtual table ought to be enough. I posit the virtual table in QObject should be dropped and the one in QObjectPrivate kept.

The fact that QObject does currently have a virtual table, exposing customizable behavior, requires QObjectPrivate to keep a pointer back to the QObject instance (q, above), so that such behavior can be invoked.

Since QObject is essentially a pointer (and it can be made just that), there's no reason it can't actually be a smart pointer and denote shared ownership of the private data. In practice, QObject would be the combination of a smart pointer and its own user-facing API. Derived classes would add their own API to QObject and hide implementations and polymorphism elsewhere.

No more heap-allocated QObject. No more containers of QObject *. No more QObject *, even.

@tsondergaard
Copy link

The QObject-QObjectPrivate construct is all about binary compatibility. If QObject was reduced to a smart-pointer, the client code would need to touch QObjectPrivate and you would lose a good deal of flexibility when it comes to making changes and keeping the binary compatibility. Essentially the private classes would become public and subject to the usual restrictions on public classes that apply when you want to maintain binary compatibility. It would not be safe to change the size of a class instance, the vtable, or inherit from the class.

@biochimia
Copy link
Author

@tsondergaard, I understand the purpose of the separation. I still think it is possible to keep the separation such that private details are hidden and can change without changes (or knowledge) further up in the hierarchy -- thus maintaining binary compatibility.

Admittedly, my earlier comment and skeleton code only offer the analysis and not an actual implementation. I still maintain one is feasible, though.

@rburchell
Copy link

as requested: "avoiding multiple heap allocations has other benefits, too (slow as all hell)"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment