-
Notifications
You must be signed in to change notification settings - Fork 1
/
cppStyleGuide
488 lines (335 loc) · 22.6 KB
/
cppStyleGuide
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
Style · C++ Best Practices .book .book-summary .book-search, .book .book-summary ul.summary { position: relative; top: auto; left: auto; right: auto; bottom: auto; } .book-summary { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; overflow-y: auto; } .book-summary > nav { flex: 1 1 auto; overflow-y: auto; overflow-y: overlay; } .gl-wrapper { margin: 0; display: block; padding: 0; position: relative; line-height: 15px; background: #fafafa; } .gl-wrapper:before { background: -webkit-linear-gradient(to bottom, rgba(250,250,250,0) 0%, #fafafa 100%); background: -moz-linear-gradient(to bottom, rgba(250,250,250,0) 0%, #fafafa 100%); top: -24px; width: 100%; height: 24px; content: ""; display: block; position: absolute; background: linear-gradient(to bottom, rgba(250,250,250,0) 0%, #fafafa 100%); } .book.color-theme-1 .gl-wrapper { background: #121212; } .book.color-theme-1 .gl-wrapper:before { background: -webkit-linear-gradient(to bottom, rgba(18,18,18,0) 0%, #121212 100%); background: -moz-linear-gradient(to bottom, rgba(18,18,18,0) 0%, #121212 100%); background: linear-gradient(to bottom, rgba(18,18,18,0) 0%, #121212 100%); } .book.color-theme-2 .gl-wrapper { background: #2e3243; } .book.color-theme-2 .gl-wrapper:before { background: -webkit-linear-gradient(to bottom, rgba(46,50,67,0) 0%, #2e3243 100%); background: -moz-linear-gradient(to bottom, rgba(46,50,67,0) 0%, #2e3243 100%); background: linear-gradient(to bottom, rgba(46,50,67,0) 0%, #2e3243 100%); } .gl-wrapper .gitbook-link { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; color: #9DAAB6; margin: 16px; display: flex; padding: 0; align-items: center; margin-left: 32px; padding-top: 8px; padding-left: 16px; border-radius: 3px; padding-right: 16px; padding-bottom: 8px; text-decoration: none; background-color: #E6ECF1; -webkit-box-align: center; } .gl-wrapper .tm-logo { margin: 0; display: block; padding: 0; font-size: 40px; } .gl-wrapper .tm-content { flex: 1; margin: 0; display: block; padding: 0; padding-left: 16px; } .gl-wrapper .tm-content .container { margin: 0; display: block; padding: 0; } .gl-wrapper .tm-content .tm-container .tm-text { font-size: 12px; font-family: "Roboto", sans-serif; font-weight: 400; line-height: 1.625; }
* [C++ Best Practices](https://legacy.gitbook.com/book/lefticus/cpp-best-practices)
* [Introduction](./)
* [Preface](01-Preface.html)
* [Use the Tools Available](02-Use_the_Tools_Available.html)
* [Style](03-Style.html)
* [Considering Safety](04-Considering_Safety.html)
* [Considering Maintainability](05-Considering_Maintainability.html)
* [Considering Portability](06-Considering_Portability.html)
* [Considering Threadability](07-Considering_Threadability.html)
* [Considering Performance](08-Considering_Performance.html)
* [Considering Correctness](09-Considering_Correctness.html)
* [Enable Scripting](10-Enable_Scripting.html)
* [Further Reading](11-Further_Reading.html)
* [Final Thoughts](12-Final_Thoughts.html)
[
Powered by **GitBook**
](https://www.gitbook.com?utm_source=public_site_legacy&utm_medium=referral&utm_campaign=trademark&utm_term=lefticus&utm_content=powered_by)
[Style](.)
==========
Style
=====
Consistency is the most important aspect of style. The second most important aspect is following a style that the average C++ programmer is used to reading.
C++ allows for arbitrary-length identifier names, so there's no reason to be terse when naming things. Use descriptive names, and be consistent in the style.
* `CamelCase`
* `snake_case`
are common examples. _snake\_case_ has the advantage that it can also work with spell checkers, if desired.
Establishing A Style Guideline
------------------------------
Whatever style guidelines you establish, be sure to implement a `.clang-format` file that specifies the style you expect. While this cannot help with naming, it is particularly important for an open source project to maintain a consistent style.
Every IDE and many editors have support for clang-format built in or easily installable with an add-in.
* VSCode: [Microsoft C/C++ extension for VS Code](https://github.com/Microsoft/vscode-cpptools)
* CLion: [https://www.jetbrains.com/help/clion/clangformat-as-alternative-formatter.html](https://www.jetbrains.com/help/clion/clangformat-as-alternative-formatter.html)
* VisualStudio [https://marketplace.visualstudio.com/items?itemName=LLVMExtensions.ClangFormat#review-details](https://marketplace.visualstudio.com/items?itemName=LLVMExtensions.ClangFormat#review-details)
* Resharper++: [https://www.jetbrains.com/help/resharper/2017.2/Using\_Clang\_Format.html](https://www.jetbrains.com/help/resharper/2017.2/Using_Clang_Format.html)
* Vim
* [https://github.com/rhysd/vim-clang-format](https://github.com/rhysd/vim-clang-format)
* [https://github.com/chiel92/vim-autoformat](https://github.com/chiel92/vim-autoformat)
* XCode: [https://github.com/travisjeffery/ClangFormat-Xcode](https://github.com/travisjeffery/ClangFormat-Xcode)
Common C++ Naming Conventions
-----------------------------
* Types start with upper case: `MyClass`.
* Functions and variables start with lower case: `myMethod`.
* Constants are all upper case: `const double PI=3.14159265358979323;`.
C++ Standard Library (and other well-known C++ libraries like [Boost](http://www.boost.org/)) use these guidelines:
* Macro names use upper case with underscores: `INT_MAX`.
* Template parameter names use camel case: `InputIterator`.
* All other names use snake case: `unordered_map`.
Distinguish Private Object Data
-------------------------------
Name private data with a `m_` prefix to distinguish it from public data. `m_` stands for "member" data.
Distinguish Function Parameters
-------------------------------
The most important thing is consistency within your codebase; this is one possibility to help with consistency.
Name function parameters with an `t_` prefix. `t_` can be thought of as "the", but the meaning is arbitrary. The point is to distinguish function parameters from other variables in scope while giving us a consistent naming strategy.
Any prefix or postfix can be chosen for your organization. This is just one example. _This suggestion is controversial, for a discussion about it see issue [#11](https://github.com/lefticus/cppbestpractices/issues/11)._
struct Size
{
int width;
int height;
Size(int t_width, int t_height) : width(t_width), height(t_height) {}
};
// This version might make sense for thread safety or something,
// but more to the point, sometimes we need to hide data, sometimes we don't.
class PrivateSize
{
public:
int width() const { return m_width; }
int height() const { return m_height; }
PrivateSize(int t_width, int t_height) : m_width(t_width), m_height(t_height) {}
private:
int m_width;
int m_height;
};
Don't Name Anything Starting With `_`
-------------------------------------
If you do, you risk colliding with names reserved for compiler and standard library implementation use:
[http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier)
Well-Formed Example
-------------------
class MyClass
{
public:
MyClass(int t_data)
: m_data(t_data)
{
}
int getData() const
{
return m_data;
}
private:
int m_data;
};
Enable Out-of-Source-Directory Builds
-------------------------------------
Make sure generated files go into an output folder that is separate from the source folder.
Use `nullptr`
-------------
C++11 introduces `nullptr` which is a special value denoting a null pointer. This should be used instead of `0` or `NULL` to indicate a null pointer.
Comments
--------
Comment blocks should use `//`, not `/* */`. Using `//` makes it much easier to comment out a block of code while debugging.
// this function does something
int myFunc()
{
}
To comment out this function block during debugging we might do:
/*
// this function does something
int myFunc()
{
}
*/
which would be impossible if the function comment header used `/* */`.
Never Use `using namespace` in a Header File
--------------------------------------------
This causes the namespace you are `using` to be pulled into the namespace of all files that include the header file. It pollutes the namespace and it may lead to name collisions in the future. Writing `using namespace` in an implementation file is fine though.
Include Guards
--------------
Header files must contain a distinctly-named include guard to avoid problems with including the same header multiple times and to prevent conflicts with headers from other projects.
#ifndef MYPROJECT_MYCLASS_HPP
#define MYPROJECT_MYCLASS_HPP
namespace MyProject {
class MyClass {
};
}
#endif
You may also consider using the `#pragma once` directive instead which is quasi-standard across many compilers. It's short and makes the intent clear.
{} Are Required for Blocks.
---------------------------
Leaving them off can lead to semantic errors in the code.
// Bad Idea
// This compiles and does what you want, but can lead to confusing
// errors if modification are made in the future and close attention
// is not paid.
for (int i = 0; i < 15; ++i)
std::cout << i << std::endl;
// Bad Idea
// The cout is not part of the loop in this case even though it appears to be.
int sum = 0;
for (int i = 0; i < 15; ++i)
++sum;
std::cout << i << std::endl;
// Good Idea
// It's clear which statements are part of the loop (or if block, or whatever).
int sum = 0;
for (int i = 0; i < 15; ++i) {
++sum;
std::cout << i << std::endl;
}
Keep Lines a Reasonable Length
------------------------------
// Bad Idea
// hard to follow
if (x && y && myFunctionThatReturnsBool() && caseNumber3 && (15 > 12 || 2 < 3)) {
}
// Good Idea
// Logical grouping, easier to read
if (x && y && myFunctionThatReturnsBool()
&& caseNumber3
&& (15 > 12 || 2 < 3)) {
}
Many projects and coding standards have a soft guideline that one should try to use less than about 80 or 100 characters per line. Such code is generally easier to read. It also makes it possible to have two separate files next to each other on one screen without having a tiny font.
Use "" for Including Local Files
--------------------------------
... `<>` is [reserved for system includes](http://blog2.emptycrate.com/content/when-use-include-verses-include).
// Bad Idea. Requires extra -I directives to the compiler
// and goes against standards.
#include <string>
#include <includes/MyHeader.hpp>
// Worse Idea
// Requires potentially even more specific -I directives and
// makes code more difficult to package and distribute.
#include <string>
#include <MyHeader.hpp>
// Good Idea
// Requires no extra params and notifies the user that the file
// is a local file.
#include <string>
#include "MyHeader.hpp"
Initialize Member Variables
---------------------------
...with the member initializer list.
For POD types, the performance of an initializer list is the same as manual initialization, but for other types there is a clear performance gain, see below.
// Bad Idea
class MyClass
{
public:
MyClass(int t_value)
{
m_value = t_value;
}
private:
int m_value;
};
// Bad Idea
// This leads to an additional constructor call for m_myOtherClass
// before the assignment.
class MyClass
{
public:
MyClass(MyOtherClass t_myOtherClass)
{
m_myOtherClass = t_myOtherClass;
}
private:
MyOtherClass m_myOtherClass;
};
// Good Idea
// There is no performance gain here but the code is cleaner.
class MyClass
{
public:
MyClass(int t_value)
: m_value(t_value)
{
}
private:
int m_value;
};
// Good Idea
// The default constructor for m_myOtherClass is never called here, so
// there is a performance gain if MyOtherClass is not is_trivially_default_constructible.
class MyClass
{
public:
MyClass(MyOtherClass t_myOtherClass)
: m_myOtherClass(t_myOtherClass)
{
}
private:
MyOtherClass m_myOtherClass;
};
In C++11 you can assign default values to each member (using `=` or using `{}`).
### Assigning default values with =
// ... //
private:
int m_value = 0; // allowed
unsigned m_value_2 = -1; // narrowing from signed to unsigned allowed
// ... //
This ensures that no constructor ever "forgets" to initialize a member object.
### Assigning default values with brace initialization
Using brace initialization does not allow narrowing at compile-time.
// Best Idea
// ... //
private:
int m_value{ 0 }; // allowed
unsigned m_value_2 { -1 }; // narrowing from signed to unsigned not allowed, leads to a compile time error
// ... //
Prefer `{}` initialization over `=` unless you have a strong reason not to.
Forgetting to initialize a member is a source of undefined behavior bugs which are often extremely hard to find.
If the member variable is not expected to change after the initialization, then mark it `const`.
class MyClass
{
public:
MyClass(int t_value)
: m_value{t_value}
{
}
private:
const int m_value{0};
};
Since a const member variable cannot be assigned a new value, such a class may not have a meaningful copy assignment operator.
Always Use Namespaces
---------------------
There is almost never a reason to declare an identifier in the global namespace. Instead, functions and classes should exist in an appropriately named namespace or in a class inside of a namespace. Identifiers which are placed in the global namespace risk conflicting with identifiers from other libraries (mostly C, which doesn't have namespaces).
Use the Correct Integer Type for Standard Library Features
----------------------------------------------------------
The standard library generally uses `std::size_t` for anything related to size. The size of `size_t` is implementation defined.
In general, using `auto` will avoid most of these issues, but not all.
Make sure you stick with the correct integer types and remain consistent with the C++ standard library. It might not warn on the platform you are currently using, but it probably will when you change platforms.
_Note that you can cause integer underflow when performing some operations on unsigned values. For example:_
std::vector<int> v1{2,3,4,5,6,7,8,9};
std::vector<int> v2{9,8,7,6,5,4,3,2,1};
const auto s1 = v1.size();
const auto s2 = v2.size();
const auto diff = s1 - s2; // diff underflows to a very large number
Use .hpp and .cpp for Your File Extensions
------------------------------------------
Ultimately this is a matter of preference, but .hpp and .cpp are widely recognized by various editors and tools. So the choice is pragmatic. Specifically, Visual Studio only automatically recognizes .cpp and .cxx for C++ files, and Vim doesn't necessarily recognize .cc as a C++ file.
One particularly large project ([OpenStudio](https://github.com/NREL/OpenStudio)) uses .hpp and .cpp for user-generated files and .hxx and .cxx for tool-generated files. Both are well recognized and having the distinction is helpful.
Never Mix Tabs and Spaces
-------------------------
Some editors like to indent with a mixture of tabs and spaces by default. This makes the code unreadable to anyone not using the exact same tab indentation settings. Configure your editor so this does not happen.
Never Put Code with Side Effects Inside an assert()
---------------------------------------------------
assert(registerSomeThing()); // make sure that registerSomeThing() returns true
The above code succeeds when making a debug build, but gets removed by the compiler when making a release build, giving you different behavior between debug and release builds. This is because `assert()` is a macro which expands to nothing in release mode.
Don't Be Afraid of Templates
----------------------------
They can help you stick to [DRY principles](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). They should be preferred to macros, because macros do not honor namespaces, etc.
Use Operator Overloads Judiciously
----------------------------------
Operator overloading was invented to enable expressive syntax. Expressive in the sense that adding two big integers looks like `a + b` and not `a.add(b)`. Another common example is `std::string`, where it is very common to concatenate two strings with `string1 + string2`.
However, you can easily create unreadable expressions using too much or wrong operator overloading. When overloading operators, there are three basic rules to follow as described [on stackoverflow](http://stackoverflow.com/questions/4421706/operator-overloading/4421708#4421708).
Specifically, you should keep these things in mind:
* Overloading `operator=()` when handling resources is a must. See [Consider the Rule of Zero](03-Style.html#consider-the-rule-of-zero) below.
* For all other operators, only overload them when they are used in a context that is commonly connected to these operators. Typical scenarios are concatenating things with +, negating expressions that can be considered "true" or "false", etc.
* Always be aware of the [operator precedence](http://en.cppreference.com/w/cpp/language/operator_precedence) and try to circumvent unintuitive constructs.
* Do not overload exotic operators such as ~ or % unless implementing a numeric type or following a well recognized syntax in specific domain.
* [Never](http://stackoverflow.com/questions/5602112/when-to-overload-the-comma-operator?answertab=votes#tab-top) overload `operator,()` (the comma operator).
* Use non-member functions `operator>>()` and `operator<<()` when dealing with streams. For example, you can overload `operator<<(std::ostream &, MyClass const &)` to enable "writing" your class into a stream, such as `std::cout` or an `std::fstream` or `std::stringstream`. The latter is often used to create a string representation of a value.
* There are more common operators to overload [described here](http://stackoverflow.com/questions/4421706/operator-overloading?answertab=votes#tab-top).
More tips regarding the implementation details of your custom operators can be found [here](http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html).
Avoid Implicit Conversions
--------------------------
### Single Parameter Constructors
Single parameter constructors can be applied at compile time to automatically convert between types. This is handy for things like `std::string(const char *)` but should be avoided in general because they can add to accidental runtime overhead.
Instead mark single parameter constructors as `explicit`, which requires them to be explicitly called.
### Conversion Operators
Similarly to single parameter constructors, conversion operators can be called by the compiler and introduce unexpected overhead. They should also be marked as `explicit`.
//bad idea
struct S {
operator int() {
return 2;
}
};
//good idea
struct S {
explicit operator int() {
return 2;
}
};
Consider the Rule of Zero
-------------------------
The Rule of Zero states that you do not provide any of the functions that the compiler can provide (copy constructor, copy assignment operator, move constructor, move assignment operator, destructor) unless the class you are constructing does some novel form of ownership.
The goal is to let the compiler provide optimal versions that are automatically maintained when more member variables are added.
[This article](http://www.nirfriedman.com/2015/06/27/cpp-rule-of-zero/) provides a background and explains techniques for implementing nearly 100% of the time.
results matching ""
===================
No results matching ""
======================
[](02-Use_the_Tools_Available.html)[](04-Considering_Safety.html)
var gitbook = gitbook || \[\]; gitbook.push(function() { gitbook.page.hasChanged({"page":{"title":"Style","level":"1.4","depth":1,"next":{"title":"Considering Safety","level":"1.5","depth":1,"path":"04-Considering\_Safety.md","ref":"04-Considering\_Safety.md","articles":\[\]},"previous":{"title":"Use the Tools Available","level":"1.3","depth":1,"path":"02-Use\_the\_Tools\_Available.md","ref":"02-Use\_the\_Tools\_Available.md","articles":\[\]},"dir":"ltr"},"config":{"plugins":\["github","comment"\],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"github":{"url":"https://github.com/lefticus/cppbestpractices"},"comment":{"highlightCommented":true},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":\["facebook","google","twitter","weibo","instapaper"\]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"github":"lefticus/cppbestpractices","theme":"default","author":"Jason Turner","pdf":{"pageNumbers":true,"fontSize":16,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"C++ Best Practices","language":"en","links":{"sidebar":{"C++ Best Practices":"https://legacy.gitbook.com/book/lefticus/cpp-best-practices"},"gitbook":true},"gitbook":"\*","description":"Best practices for ensuring high quality C++ code. This book has now inspired a video series from O'Reilly called \\"Learning C++ Best Practices.\\""},"file":{"path":"03-Style.md","mtime":"2020-04-13T03:38:48.000Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2020-04-13T03:38:29.864Z"},"basePath":".","book":{"language":""}}); });