Increase your code value with the C++ style guide and coding standards: Part 1

Anurag Patel
Geek Farmer
Published in
9 min readApr 2, 2019

--

In this article, I am going to discuss the C++ style guide. This is the first part of this series.

  1. C++ style guides Part 1
  2. C++ Style guides Part 2

I am going through stepwise step on the basis of following

  • Getting Started
  • Header Files
  • Classes
  • Scoping
  • Function

And in the next part of this series, I will describe the following features of C++ and their style guide.

  • Class hierarchies
  • Memory
  • Naming and comments
  • Other Features of C++
  • Exceptions

Let’s start our journey….

Getting Started

Why we want a c++ style guide?

Don’t learn anything before asking yourself why I need this. I learn about C++ style guide because I don’t think that my code has a coding standard and anyone can’t understand it when they first time sees it. Coding standards and C++ style guide help me a lot to provide value to my code. There are lots of rules in coding, but before applying them all in your code, you need to understand what goals each rule is served and what sort of argument or alternative would be necessary to change a rule in the guide.

The intent of this article is to provide you a guidance and a reasonable restriction to your code about coding rules. The main aim of C++ coding standards is to provide a set of rules for using C++ for a particular purpose in a particular environment. There can’t be one coding standards and style guide for all uses and all users.

A very simple example to reverse the elements of the array.

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
vector<double> v;
double d;
while(cin>>d) v.push_back(d); // read elements
if (!cin.eof()) { // check if input failed
cerr << "format error\n";
return 1; // error return
}
cout << "read " << v.size() << " elements\n"; reverse(v.begin(),v.end());
cout << "elements in reverse order:\n";
for (int i = 0; i<v.size(); ++i) cout << v[i] << '\n';
return 0; // success return
}

Observations to write a standard code….

  1. This is a standard C++ program using standard template library (STL) and declared namespace std in the header without.h suffix.
  2. Reading input into a vector guarantees that you don’t overflow some arbitrary buffer, but an array can’t handle this.
  3. The !cin.eof() is a test of the stream’s format. Specifically, it tests whether the loop ended by finding end-of-file.
  4. Here, I don’t need to count the vector size because a vector knows its size.
  5. A vector keeps track of the memory it uses to store its elements. When a vector needs more memory for elements, it allocates more; when a vector goes out of scope, that it frees that memory. So, don’t need to take care of memory management.
  6. Here, in this program, I use EOF(end-of-file) character, which doesn’t recognize if you are on a Windows machine.

Header Files

Every C++ source file contains “.h” associated file called header files. These header files can make a great impact on your code. Header files can make a huge difference to the readability, size and performance of your code.

  1. #Define Guard….. Header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <project>_<path>_<file>_h_.

To guarantee uniqueness, they should be based on the full path in a project’s source tree. For example, the file project1/src/main/foo.h in project project1 should have the following guard:

#ifndef PROJECT1_MAIN_FOO_H_
#define PROJECT1_MAIN_FOO_H_
...#endif // PROJECT1_MAIN_FOO_H_

2. Header files should be self-contained (compile on their own) and end in .h.

3. Avoid using Forward declarations where possible. Just #include the headers you need. A “forward declaration” is a declaration of a class, function, or template without an associated definition. As #include uses more compile time by forcing the compiler to open more files and process more input, but forward declaration save compile time and save unnecessary re-compilation. It can be difficult to determine whether a forward declaration or a full #include is needed. Replacing an #include with a forward declaration can silently change the meaning of the code. Try to avoid forward declarations of entities defined in another project.

4. When any function is about 10 lines or fewer than 10 then only defines that function inline. You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism. Inline a function can generate more efficient object code, as long as the inlined function is small. But inline function makes the program slower, which is depending on function size.

It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. Usually, recursive functions should not be inline. The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.

Classes

Object and classes are a fundamental part of the code in C++.

  1. Avoid Virtual method calls in constructors and also avoid initialization because it can be failed if you can’t signal an error. It is possible to perform arbitrary initialization in the body of the constructor. If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future modification to your class can quietly introduce this problem even if your class is not currently sub-classes, causing much confusion. So, constructors should never call virtual functions.
  2. Do not define implicit conversions. Use the explicit keyword for conversion operators and single-argument constructors.
  3. The protection of every base class should be specifically stated. When one class is derived from another, the default protection is private. Example:
class D : B {}; // D is privately derived from B

This is counter-intuitive as public inheritance is more commonly used. Thus, the compiler will generate a warning for unspecified protection levels. It is best to specifically state whether the inheritance is public, protected, or private. Example:

class D : public b {};

4. A class’s public API should make explicit whether the class is copyable, move-only, or neither copyable nor movable. Support copying and/or moving if these operations are clear and meaningful for your type.

5. Use a struct only for passive objects that carry data and may have associated constants; everything else is a class.

6. Every class with one or more virtual functions should have a virtual destructor. Since a derived class object may be deleted through a base class pointer, the base class must have a virtual destructor to ensure that the derived class destructor gets called. Otherwise, only the base class destructor will be called, resulting in a probable memory leak.

7. Composition is often more appropriate than inheritance. When using inheritance, make it public and if you want to do private inheritance, you should be including an instance of the base class as a member instead.

8. Multiple inheritances is allowed only when all super-classes, with the possible exception of the first one, are pure interfaces. In order to ensure that they remain pure interfaces, they must end with the Interface suffix. But a class may end with Interface only if it meets the above requirements. We do not require the converse, however: classes that meet the above requirements are not required to end with Interface.

Function

  1. Except for constructors, function overloading should be reserved for functions in which the meaning of the overload is clear. Overloading and calling overloaded functions can decrease readability because it may not be clear to the reader, which version of the function is being called. Functions should not be overloaded on the const-ness of the input arguments unless absolutely necessary and thoroughly documented. You may overload a function when there are no semantic differences between variants, or when the differences are clear at the call-site. If you are overloading a function to support a variable number of arguments of the same type, consider making it take a std::vector so that the user can use an initializer list to specify the arguments.
  2. Default arguments are banned on virtual functions, where they don’t work properly, and in cases where the specified default might not evaluate to the same value depending on when it was evaluated. For example, don’t write
void f(int n = counter++);

In some other cases, default arguments can improve the readability of their function declarations enough to overcome the downsides above, so they are allowed. When in doubt, use overloads.

3. Pass double, int, unsigned, and bool as such, no need for const modifiers or passing by reference (passing primitives by value is more efficient). Pass all other function arguments as const& references whenever possible to avoid unnecessary copying.

void Foo(const string &in, string *out);

Scoping

  1. Namespace, class, variable, and function names should be sufficiently descriptive that they convey their meaning and typical usage. We prefer to spell out words rather than use abbreviations to maintain clarity and readability. Names should be as specific to the context as possible, e.g. Coordinate rather than Number.
  2. With few exceptions, place code in a namespace. Namespaces should have unique names based on the project name, and possibly its path. Do not use using-directives (e.g. using namespace foo). Do not use inline namespaces. Namespaces provide a method for preventing name conflicts in large programs while allowing the most code to use reasonably short names.
  3. Prefer placing nonmember functions in a namespace; use completely global functions rarely. Do not use a class simply to group static functions. Static methods of a class should generally be closely related to instances of the class or the class’s static data.
  4. Sometimes it is useful to define a function not bound to a class instance. Such a function can be either a static member or a nonmember function. Nonmember functions should not depend on external variables, and should nearly always exist in a namespace. Do not create classes only to group static member functions; this is no different than just giving the function names a common prefix, and such grouping is usually unnecessary anyway.
  5. C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.:
int i;
i = f(); // Bad -- initialization separate from declaration.
int j = g(); // Good -- declaration has initialization.*****************************************************************std::vector<int> v;
v.push_back(1); // Prefer initializing using brace initialization.
v.push_back(2);
std::vector<int> v = {1, 2}; // Good -- v starts initialized.

6. Variables needed for if, while and for statements should normally be declared within those statements, so that such variables are confined to those scopes. E.g.:

while (const char* p = strchr(str, '/')) str = p + 1;

7. There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.

// Inefficient implementation:
for (int i = 0; i < 1000000; ++i) {
Foo f; // My ctor and dtor get called 1000000 times each.
f.DoSomething(i);
}

It may be more efficient to declare such a variable used in a loop outside that loop:

Foo f;  // My ctor and dtor get called once each.
for (int i = 0; i < 1000000; ++i) {
f.DoSomething(i);
}

Conclusion

In this article a give you just a basic idea about coding standards and style guide. There are lots of more for learning in coding standards. But this article gives you guidance about how you can go further and learn. For more details and to increase your knowledge, please look at below references.

Thanks for reading…..

References

http://www.parashift.com/c++-faq/

--

--