I have a class that has some data members that I want to be hidden from the caller (because including the headers for their types significantly increases the compile time, and it would require every project using this class to add an additional path to their include paths).
This class uses QSharedDataPointer to store this data. This way it can be copied by using the default copy constructor.
The basic structure of this class is:
class MyClass {
private:
QSharedDataPointer<MySharedClassData> m_data;
};
Is there any fancy trick to do this without defining MySharedClassData (which inherits from QSharedData) in the same header file? Or is there any other good way of hiding data fields?
I've already tried a forward declaration of MySharedClassData but this didn't work (despite the fact that m_data is private).
The only solution I can currently thing of is to declare m_data as QSharedDataPointer<QSharedData> but then I need to cast the data member every time I want to access it. Is there a better solution?
The forward declaration should be working as long as you constructor and destructor are not defined in the header. The following class compiles on my computer:
#ifndef MAIN_WINDOW_HXX
#define MAIN_WINDOW_HXX
#include <QMainWindow>
#include <ui_MainWindow.h>
#include <QSharedDataPointer>
class MySharedClassData;
class MainWindow : public QMainWindow, private Ui_MainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
virtual ~MainWindow();
QSharedDataPointer<MySharedClassData> m_data;
};
#endif
If you try to inline your constructor/destructor, then you might receive a: C2027: use of undefined type 'type' under VS.
Yes. There's no really fancy trick required. However, all methods that do need MySharedClassData must be defined after the definition of MySharedClassData. If you move the class definition to a .cpp file, the methods have to be moved there too.
In general, if you want to use the pimpl idiom with a smart pointer to a forward-declared impl (rather than managing the impl object manually), you need a smart pointer with an out-of-line deleter, like boost::shared_ptr (you should be able to use std::unique_ptr with a custom deleter, but I haven't tried).
The requirement is that you can instantiate the smart pointer template without seeing the impl class destructor: this rules out std::auto_ptr for example.
I don't know whether QSharedDataPointer meets the requirement or not, but tibur seems to say it does.
Related
I'm working on my first non-trivial project using the Qt Framework and to help maintain consistency across documents and to ensure I don't forget some small requirement I've decided to make a template document demonstrating the member functions, macros, etc. needed to subclass QObject. I also want to be able to fully utilize the Meta Object system. Am I missing anything or misunderstanding anything? Also feel free to give any general C++ critiques as necessary.
I'm especially concerned with the issue of whether or not to include a copy constructor. Is that only necessary for classes not derived from QObject?
Requirements (links are to the document I got the requirements from)
Public Default Constructor (link)
Public Copy Constructor (see 1) (but conflicts with this)
Public Destructor (see 1)
Use the Q_OBJECT Macro (link)
Ensure Properties Are Accessible with the Q_PROPERTY(...) Macro (link)
Declare the Type with Q_DECLARE_METATYPE(T) in the Header (link) (link)
Declare any Enums Used with Q_ENUM(E) in the Header (link)
Template Header
// Include guards
#ifndef CLASSNAME_H
#define CLASSNAME_H
// Include statements
#include <QObject>
#include <T.h>
// Enum definition
enum E{
E0,
E1,
E2
};
// Q_ENUM Macro
Q_ENUM(E)
// Class declaration
class ClassName : public QObject
{
// Q_OBJECT Macro
Q_OBJECT
// Q_PROPERTY Macros
Q_PROPERTY(T* memberA READ memberA WRITE setMemberA NOTIFY memberAChanged)
Q_PROPERTY(int memberB READ memberB WRITE setMemberB NOTIFY memberBChanged)
Q_PROPERTY(E memberC READ memberC WRITE setMemberC RESET resetMemberC)
public:
// Constructors and Destructor
ClassName(QObject *parent = nullptr);
ClassName() = default;
ClassName(const ClassName&) = default;
~ClassName();
// Getters
T* memberA() const {return m_memberA;}
int memberB() const {return m_memberB;}
E memberC() const {return m_memberC;}
// Setters
void setMemberA(T* newA);
void setMemberB(int newB);
void setMemberC(E newC);
signals:
void memberAChanged(T*);
void memberBChanged(int);
public slots:
void resetMemberC();
private slots:
private:
// Data Members
T* m_memberA;
int m_memberB;
E m_memberC;
};
// Meta Object Type Declaration
Q_DECLARE_METATYPE(TypeName);
// End include guard
#endif // CLASSNAME_H
The source file to accompany this header would likely be trivial, so I won't include it here. Though if anyone thinks it would be helpful to demonstrate some requirement or functionality, I'd be happy to write it out.
As Jeremy Friesner suggested, the requirements are not that strict. The situation is more like this:
If your class uses signals and/or slots, it must both have the Q_OBJECT macro and be derived from QObject,
If it only uses other meta-object functionality, such as Q_PROPERTY declarations, it can use the Q_GADGET macro and need not be derived from QObject,
If it doesn't need any of that, but should still be compatible with Qt templates like QVariant, it should be declared with Q_DECLARE_METATYPE. The same applies to enums and Q_ENUM.
A Q_PROPERTY/Q_INVOKABLE interface is only really needed if you need your class to be interoperable with QML code.
As for your other question, yes that is an important difference between QObjects and non-QObjects. The metatype must be copyable, which is why that is required of types you manually declare as metatypes, and also why the system instead uses pointers for QObject types, which are not copyable themselves. A minimal QObject declaration could start like this:
#ifndef CLASSNAME_H
#define CLASSNAME_H
#include <QObject>
// Enums in the global namespace cannot be registered; they must be enclosed
// in a class and registered with Q_ENUM, or in a namespace declared as
// Q_NAMESPACE and registered with Q_ENUM_NS
class ClassName : public QObject
{
Q_OBJECT
public:
// Default constructor, with explicit specifier to prevent accidental
// implicit conversion from a QObject*
explicit ClassName(QObject *parent = nullptr);
};
// ClassName* is automatically declared as a metatype
#endif // CLASSNAME_H
In general I'd recommend the "rule of zero": if possible, do not declare a destructor, nor any copy and move operations, and leave them to the compiler. Of course I also recommend all of the other guidelines there, if you have the time!
Here is a base class:
class Base
{
public:
int foo;
// ...
};
This class is declared in a header file that I cannot change because I don't have the rights to do so.
Here is a derived class:
class Derived : public Base
{
public:
// provide alias to Base::foo
// ...
};
Is there a way I can provide an alternate name that refers to Base::foo inside class Derived without using pointers or references?
In other words, is there any way to provide an alias without adding any extra class members to Derived?
Also, if conditional compilation directives can be avoided, that would be ideal.
No, there is no renaming feature (though there was some work done on such a feature in the early days but it was dropped).
There's no way to do what you want other than adding the desired name as a member to Derived, and forwarding it to the base class function. Note however that this can be inline and should be absorbed by the compiler so it won't hurt performance.
I am trying to build a shared library with Qt 5.6.0 and i would like to use Pimpl.
I have one class to export and this class inherits from QGraphicsScene, which itself inherits from QObject :
// CustomScene.h
#include "customscene_global.h" // recommended header from Qt
#include <QGraphicsScene> // do not compile if not present
// forward declarations
class CustomSceneImpl;
template <typename T> class QList;
class QString;
template <class Key, class T> class QMap;
// some other classes ...
class CustomScene : public QGraphicsScene
{
Q_OBJECT
public :
// some public functions
signals:
// some custom signals
public slots:
// some custom public slots
private:
CustomSceneImpl *m_pimpl; // pointer to private members
};
Everything's is fine if i #include <QGraphicsScene> in this header but i would prefer to forward declare QGraphicsScene. However i get the following compile error if i do that :
error: invalid use of incomplete type 'struct QGraphicsScene'
I guess i am doing something wrong with the QObject Macro, that is not recognized, and with the moc in general.
I know there are dedicated Qt Macro to use Pimpl ( Q_D, .. ) but i did not really understand how to use them and if it would fix this issue.
Thanks for your help.
Everything's is fine if i #include QGraphicsScene in this header but i would prefer to forward declare QGraphicsScene.
you cannot do it as you inherit from QGraphicsScene:
class CustomScene : public QGraphicsScene
btw. this error has nothing to do with the way you implemented PIMPL idiom (actually it looks fine), inheritance from incomplete type is simply not possible as compiler needs to know the size / layout of your base class.
Since a forward declared class has no information about the content, you can't use it for inheritance.
You can use PIMPL idiom for composition, not for inheritance. If you need to inherit from QGraphicsScene, you cannot forward declare it. You have to include its full definition.
I am new at building distributable libraries written in C++ and I am getting a bit lost.
I have created a .cpp file with wrappers for all functions I want the library to offer users, and I have written 2 .h files, one public and one private. Below is a dummy example of my header files:
public.h:
class myclass
{
public:
public_function();
private:
}
private.h:
class myclass
{
public:
public_function();
private:
anotherClass instanceofClass;
}
Note that the implementation of public_function() uses the "instanceofClass" in the code.
I have been able to compile with no problem the code using the private class and to compile and link the library with external programs using the public header and the compiled library. When executing that code, though, I am getting segmentation faults that I suspect have to do with lack of proper initialization of "instanceofClass".
Am I doing the right thing? Am I forced to instantiate "instanceofClass" inside the implementation of public_function() for it to be initialized properly, or is there anything else I should do instead?
Thanks a lot.
You can't declare the same class 'myclass' in two different ways. There has to be a single class definition. If you want to hide the implementation's API you want to use the 'Pimpl' idiom. So your public class has a single pointer to a private class. For example:
public.h
class myclass_private;
class myclass {
private:
myclass_private* pimpl;
public:
myclass();
void public_function();
};
public.cpp
myclass::myclass() {
pimpl = new myclass_private;
}
void myclass::public_function() {
pimpl->private_function();
}
private.h
class myclass_private {
public:
void private_function();
};
The myclass defined in public.h has no members, and is therefore sized 1 byte. The myclass defined in private.h encapsulates anotherClass, and is therefore whatever size anotherClass is. This inconsistency is the root of your problem.
What you ought to do is have only one header, and use a pointer (which doesn't require a class definition) to enable hiding the implementation of anotherClass. I'll repeat Joachim's link to the pimpl idiom for elaboration.
The definition of a class shall not changr between different translation units. This is one of the aspects of the One Definition Rule. What you might want to donis to define the publicly visible class to have a pointer to a private implementation: the Pimpl Idiom:
class Public {
public:
...
private:
struct Impl;
Impl* impl_;
};
The struct Impl would only be defined in the implementation file.
Your class lacks a proper constructor, which means that the compiler will provide a default one based on the content of the class definition. If that definition isn't consistent accross all the code, it won't get initialized the same way everywhere, and some data may be missing.
If you want to hide the implementation details of instanceofClass, just do a forward declaration in the header (the private header you're providing is correct, you can use it as your public one), and provide an implementation somewhere in your code.
I have a basic question that has bothered me for sometime.
When using a Class within a Class I can define the header of the Class I want to use in the header file. I have seen two ways of doing this and would like to know the difference between the two methods?
ex1
#include "ClassA.h"
class ClassB {
public:
ClassB();
~ClassB();
ClassA* a;
};
#endif
ex2 Here is the other way of doing it. The ClassA Header would be defined in ClassB source file.
class ClassA;
class ClassB {
public:
ClassB();
~ClassB();
ClassA* a;
};
#endif
What are the differences with these two methods?
The comlpete layout of the classA is known to the compiler when you include the class definition.
The second syntax is called Forward declaration and now classA is an Incomplete type for the compiler.
For an Incomplete type,
You can:
Declare a member to be a pointer or a reference to the incomplete type.
Declare functions or methods which accepts/return incomplete types.
Define functions or methods which accepts/return pointers/references to the incomplete type (but without using its members)
But You cannot:
Use it as a base class.
Use it to declare a member.
Define functions or methods using this type.
Use its methods or fields, in fact trying to dereference a variable with incomplete type.
So Forward Declaring the class might work faster, because the complier does not have to include the entire code in that header file but it restricts how you can use the type, since it becomes an Incomplete type.
The second method only allows you to create pointers to ClassA, as it's size is unknown. It may however compile faster as the header for the full definition for ClassA is not included.
The latter is a forward declaration. This way you can declare a pointer or reference to a class, even though you have not yet fully declared it. This can be used to resolve cyclic dependencies. What if, in your first example, A also wants to use a pointer to B. This wouldn't work, because when A is declared, B is not known yet. To solve this, you can use a forward declaration to tell the compiler that there is a class B, and you will tell it later what it looks like.