2. 1972:
C
1983:
Renamed
to C++
What is it good for?
C++ is very flexible and unfortunately also quite complicated
Basically let’s us do everything, even if it is dangerous
You can find a lot of “advices” on the web, which are just bad
Bjarne Stroustrup: “Within C++ is a smaller, simpler, safer language struggling to get out”
1979:
C with
Classes
1998:
New ISO
Standard
C++98
2003:
New ISO
Standard
C++03
2011:
New ISO
Standard
C++11
(inf. C++0x)
2014:
New ISO
Standard
C++14
2017:
New ISO
Standard
C++17
Bjarne StroustrupKen Thompson (left)
Dennis Ritchie (right)
3. 1972:
C
1983:
Renamed
to C++
1979:
C with
Classes
1998:
New ISO
Standard
C++98
2003:
New ISO
Standard
C++03
2011:
New ISO
Standard
C++11
(inf. C++0x)
2014:
New ISO
Standard
C++14
2017:
New ISO
Standard
C++17
What is it good for?
Use a subset of safe, modern C++ features and apply rules to avoid crashes,
undefined behaviour and similar.
Core Guidelines
4. How can I use them?
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
Work in progress, sometimes controversial + you can also contribute!
The document gives advices how to write good C++ code
Supports you with a library, the Guideline Support Library (GSL)
Tools that can check these rules
VisualStudio with an additional Nuget package (via the Analyze function)
Clang-tidy (via command line)
Let’s take a look at some samples (not complete, nor ordered by importance)
5. Samples: P.1 Express ideas directly in code
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#p1-express-ideas-directly-in-code
class Date {
public:
Month month() const; // do
int month(); // don't
// ...
};
//don't
void f(vector<string>& v, string val)
{
int index = -1;
for (int i = 0; i < v.size(); ++i)
if (v[i] == val) {
index = i;
break;
}
// ...
}
//do
void f(vector<string>& v, string val)
{
auto p = find(begin(v), end(v), val);
// ...
}
6. Samples: P.5 Prefer compile-time checking to
run-time checking
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#p5-prefer-compile-time-checking-to-run-time-checking
//don’t
void read(int* p, int n); // read max n integers into *p
//do
void read(span<int> r); // read into the range of integers r
//the span<T> template is supplied via the GSL
7. Samples: I.4 Make interfaces precisely and
strongly typed
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i4-make-interfaces-precisely-and-strongly-typed
void pass(void* data); // void* is suspicious, always
//don’t
void draw_rect(int, int, int, int); // great opportunities for mistakes
draw_rect(p.x, p.y, 10, 20); // what does 10, 20 mean?
//do
void draw_rectangle(Point top_left, Point bottom_right);
void draw_rectangle(Point top_left, Size height_width);
draw_rectangle(p, Point{10, 20}); // two corners
draw_rectangle(p, Size{10, 20}); // one corner and a (height, width) pair
8. Samples: I.6 Prefer Expects() for expressing preconditions
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i6-prefer-expects-for-expressing-preconditions
int area(int height, int width)
{
Expects(height > 0 && width > 0); // good
if (height <= 0 || width <= 0) my_error(); // obscure
// ...
}
//Preconditions can be stated in many ways, including comments, if-
//statements, and assert(). This can make them hard to distinguish from
//ordinary code, hard to update, hard to manipulate by tools, and may //have
the wrong semantics (do you always want to abort in debug mode //and check
nothing in productions runs?).
//Preconditions should be part of the interface rather than part of the
//implementation, but we don't yet have the language facilities to do
//that.
//The Expects(cond) macro is supplied via the GSL
9. Samples: I.9 If an interface is a template, document its
parameters using concepts
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i9-if-an-interface-is-a-template-document-its-parameters-using-concepts
template<typename Iter, typename Val>
// requires InputIterator<Iter> && EqualityComparable<ValueType<Iter>>, Val>
Iter find(Iter first, Iter last, Val v)
{
// ...
}
//Note:
//Soon most compilers might be able to check requires clauses and you can
//simply remove once the comment double slashes //.
10. Samples: I.11 Never transfer ownership by a raw pointer
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i11-never-transfer-ownership-by-a-raw-pointer-t
// don't
X* compute(args)
{
X* res = new X{};
// ...
return res;
}
//do
vector<double> compute(args)
{
vector<double> res(10000);
// ...
return res;
}
11. Samples: I.12 Declare a pointer that must not be null
as not_null
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i12-declare-a-pointer-that-must-not-be-null-as-not_null
int length(const char* p); // is length(nullptr) valid?
length(nullptr); // OK?
int length(not_null<const char*> p);// better: p cannot be nullptr
int length(const char* p); // we must assume that p can be nullptr
//the not_null<T> template is supplied via the GSL
12. Samples: F.3 Keep functions short and simple
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f3-keep-functions-short-and-simple
//There is a lot of discussion how many lines of code are a “good” size for
//function/method. Usually if it does not fit on the screen any more you
//should start to think how to make it simpler.
//Without naming a reference I hope that we can agree that anything beyond
//250 lines is maybe too long. People also argue that loc is not a good
//way of measuring code complexity (however it is easy to check).
13. Samples: F.51 Where there is a choice, prefer default
arguments over overloading
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f51-where-there-is-a-choice-prefer-default-arguments-over-overloading
//so this is better
void print(const string& s, format f = {});
//than
void print(const string& s); // use default format
void print(const string& s, format f);
//There is no guarantee that a set of overloaded functions all implement the same semantics.
//The use of default arguments can avoid code replication.
14. Samples: C.20 If you can avoid defining default
operations, do
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c20-if-you-can-avoid-defining-default-operations-do
struct Named_map {
public:
// ... no default operations declared ...
private:
string name;
map<int, int> rep;
};
Named_map nm; // default construct
Named_map nm2 {nm}; // copy construct
//defining no constructor is also known as “rule of zero”
15. Samples: C.45 Don't define a default constructor that only
initializes data members; use in-class member initializers instead
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c45-dont-define-a-default-constructor-that-only-initializes-data-members-
use-in-class-member-initializers-instead
class X1 { // BAD: doesn't use member initializers
string s;
int i;
public:
X1() :s{"default"}, i{1} { }
// ...
};
class X2 {
string s = "default";
int i = 1;
public:
// use compiler-generated default constructor
// ...
};
16. Samples: C.131 Avoid trivial getters and setters
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c131-avoid-trivial-getters-and-setters
class point {
int x;
int y;
public:
point(int xx, int yy) : x{xx}, y{yy} { }
int get_x() { return x; }
void set_x(int xx) { x = xx; }
int get_y() { return y; }
void set_y(int yy) { y = yy; }
// no behavioral member functions
};
//basically the same like this:
struct point {
int x = 0;
int y = 0;
};
17. Samples: C.140 Do not provide different default
arguments for a virtual function and an overrider
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c140-do-not-provide-different-default-arguments-for-a-virtual-function-and-an-overrider
class base {
public:
virtual int multiply(int value, int factor = 2) = 0;
};
class derived : public base {
public:
int multiply(int value, int factor = 10) override;
};
derived dr;
base& bs = dr;
bs.multiply(10); // these two calls will call the same function but
dr.multiply(10); // with different arguments and so different results
18. Samples: Enum.1 Prefer enums over macros
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#enum-enumerations
//don’t
//Webcolors in header x
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
//also bad
//Productinfo in header y
#define RED 0
#define PURPLE 1
#define BLUE 2
int webby = BLUE; // wrong header could now mean “webby == 2”
//do
enum class Webcolor { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum class Productinfo { red = 0, purple = 1, blue = 2 };
int webby = blue; // error: be specific (this is a class enum, not a plain enum)
Webcolor webby = Webcolor::blue;
19. Samples: ES.5 Keep scopes small
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es5-keep-scopes-small
void use()
{
int i;
// bad: i is needlessly accessible after loop
for (i = 0; i < 20; ++i) { /* ... */ }
// no intended use of i here
// good: i is local to for-loop
for (int i = 0; i < 20; ++i) { /* ... */ }
// good: pc is local to if-statement
if (auto pc = dynamic_cast<Circle*>(ps)) {
// ... deal with Circle ...
}
else {
// ... handle error ...
}
}
20. Samples: ES.20 Always initialize an object
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es20-always-initialize-an-object
void use(int arg)
{
int i; // bad: uninitialized variable
// ...
i = 7; // initialize i
}
//No, i = 7 does not initialize i; it assigns to it.
//Also, i can be read in the ... part. Better:
void use(int arg)
{
int i = 7; // OK: initialized
string s; // OK: default initialized
// ...
}
21. Samples: ES.50 Don't cast away const
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es50-dont-cast-away-const
void bad_function(const MyClass* cInstance) {
...
if(cInstance.max_bytes>MAX_VAL) {
//bad sample
MyClass* instance = const_cast<Myclass*>(cInstance);
++instance.max_bytes;
}
}
//if really needed MyClass can be change, eg:
class MyCLass {
public:
mutable std::int32_t max_bytes; //only a subset can be changed,
//not the whole class
...
}
23. Samples: R.5 Don't heap-allocate unnecessarily
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r5-dont-heap-allocate-unnecessarily
void f(int n)
{
auto p = new Gadget{n};
// ...
delete p;
}
//Instead, use a local variable:
void f(int n)
{
Gadget g{n};
// ...
}
24. Samples: R.11 Avoid calling new and delete explicitly
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r11-avoid-calling-new-and-delete-explicitly
auto myObject = new MyObject{“MyObj”}; //bad
auto myObjectUnique = std::make_unique<MyObject>(“MyObj”); //better
//in case you need to share the resource in your application:
auto myObjectShared = std::make_shared<MyObject>(“MyObj”);
//most libraries come with some kind of smart pointers
//doing more or less the same (if you cannot use STL)
25. Samples: T.1 Use templates to raise the level o
abstraction of code
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#t1-use-templates-to-raise-the-level-of-abstraction-of-code
template<typename T, typename A>
// requires Arithmetic<T>
A sum(vector<T>& v, A s)
{
for (auto x : v) s += x;
return s;
}
//For additional generality and reusability, we could also use a
//more general Container or Range concept instead of committing to
//only one container, vector.
26. How can I use the GSL?
The GSL has a public specification that can be implemented by anyone
Currently we can use the Microsoft implementation https://github.com/Microsoft/GSL
There are other versions too (eg. for C++11), however not updated very frequently
27. Tools to enforce the rules
A) CppCoreCheck can be installed as package to any C++ project via the following
command in the VS package manager console (an then run “Analyze”):
B) Install latest clang from http://llvm.org/releases/download.html and use clang-tidy:
(Unfortunately problematic with Windows headers)
Both tools check more than only the C++ Core Guidelines – it’s a good idea in general
to use a static code analyzer to avoid errors.
PM> Install-Package Microsoft.CppCoreCheck
$ clang-tidy SOURCE --checks=all -extra-arg=-std=c++14 –extra-arg=-fexceptions