1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
|
Coding Style
============
Naming
------
We try to follow Qt naming convention whenever possible:
Classes are CamelCase.
Variables are camelCase.
Files are lowercase.h and lowercase.cpp.
Members of a class are prefixed with "m_".
Rationale: When reading the code of a method, it makes it easy to know whether
a variable is a local or a member variable.
Accessors are named foo() (not getFoo())
Setters are named setFoo(...)
Formatting
----------
Indentation is 4 spaces, no tabs.
Opening braces are on the same line as the code, except for classes and
functions. Example:
class Foo
{
public:
void method();
};
void Foo::method()
{
if (something()) {
doThis();
doThat();
}
}
Rationale:
Having opening braces on the same line ensure we can put more code
on a screen. For some reason it seems people highly dislike having opening
brace on the same line as class definitions and function implementations, hence
they are on their own line in this case.
Even one-line blocks should be enclosed in braces.
Do not do that:
if (something())
doThis();
else
doThat();
for (int x=0; x < 10; ++x)
inLoop(x);
Do this:
if (something()) {
doThis();
} else {
doThat();
}
for (int x=0; x < 10; ++x) {
inLoop(x);
}
Rationale: always having braces is only one line longer and avoids mistakes like that:
if (something())
doThis();
else
doThat();
doThatAsWell(); // Always executed, but author probably wanted it
// to be run in the else part only
It also avoids cluttering diffs with the add/removal of braces.
Best Practices
==============
Pass arguments by const references instead of values
----------------------------------------------------
Even if some Qt classes like QString are implicitly shared and meant to be used
like built-in types, they should be passed as const references instead of
values. Do not do that:
class Foo
{
void method(QString);
};
Do this:
class Foo
{
void method(const QString&);
};
Rationale:
- It is still faster to pass a reference than to call a constructor
- It helps reducing the amount of #include in header files (see "Clean
Headers")
- Qt does it this way, so it must be right! ;)
Const correctness
-----------------
If a method does not modify an object, mark it const. This is important for (at
least) two reasons:
- It can potentially help the compiler to produce more efficient code (if the
code is in another .o the compiler has no way to know whether a method is
going to modify an object)
- It makes it possible to call this method on a const ref or a const pointer.
For example:
class Foo
{
public:
int computeSomething() const;
};
void someOtherFunction(const Foo& foo)
{
int result = foo.computeSomething();
}
If computeSomething() had not been declared const, someOtherFunction() would
not build.
Clean Headers
-------------
Header files should contain the minimum amount of #include.
This can be done by using forward declarations of classes, passing const
references instead of values or using pimpls (see "Pimpls").
Reducing includes speeds up compilation time because it avoids propagating
changes. Consider these files:
// a.h
#include <b.h>
class A
{
// ...
private:
void function(B);
};
// c.cpp
#include <a.h>
...
When a change is made in b.h, c.cpp has to be rebuild even if it is only used
in the private part of the A class.
If a.h is changed to:
class B;
class A
{
public:
private:
void function(const B&);
};
Then c.cpp won't be rebuilt when b.h changes.
Having clean headers also avoids propagating other component include paths. In
the previous example, if b.h is in a different installed dir, then c.cpp has to
be build with the include path to find b.h, even if it does not need it.
Pimpls
------
Pimpl stands for "Private Implementation". The idea is to put all private code
in an opaque class, like this:
// foo.h
class FooPrivate;
class Foo
{
public:
Foo();
~Foo();
private:
FooPrivate* const d;
};
// foo.cpp
class FooPrivate
{
public:
int m_abc;
QString m_def;
}
Foo::Foo()
: d(new FooPrivate)
{}
Foo::~Foo()
{
delete d;
}
Rationale:
- It makes it possible to add/remove private members to a class without
breaking binary compatibility (BC). This is *very* important for libraries.
- It helps reducing compilation time because modifying the private part of a
class only requires changes in the .cpp file.
Drawback:
Slots cannot easily be put in a pimpl so they must still be private members of
the class (but adding/removing them does not break BC).
Note: Using "d" as the name of the pimpl is common practice in Qt and KDE code
(It stands for "data" IIRC).
Being a one letter variable reduces the cluttering when public and protected
code accesses private members and methods.
QT_NO_KEYWORD
-------------
Qt provides two ways to define signals/slots:
class Foo
{
Q_OBJECT
signals:
void somethingHappened();
public slots:
void doSomething();
};
class Foo
{
Q_OBJECT
Q_SIGNALS:
void somethingHappened();
public Q_SLOTS:
void doSomething();
};
The first version is problematic because "signals" is actually a #define for
"protected". This can cause build failures with at least some of the latest gio
stuff, which has a structure containing a field named "signals".
To avoid this, one can build with the QT_NO_KEYWORD #define, which disable the
"signals" and "slots" #defines. Instead one has to use Q_SIGNALS and Q_SLOTS.
We should ensure our code builds with the QT_NO_KEYWORD #define.
|