Posts

Showing posts from 2014

#9. Keep functions short and cohesive

A function should have one specific purpose. And it should achieve its purpose by means of a cohesive structure. A function can be structured in one of these ways: A sequence of actions. A condition . A repetition of certain actions. These control flow structures can be combined to form more sophisticated units. For example, a function may be a sequence of actions, with some of them being executed only under some conditions, and some other being loops. A certain degree of composition may be useful, but if you're not very strict, you'll soon find yourself writing 50-line functions which lack any structure. Software scientists have long attempted to measure the complexity of code. Cyclomatic complexity measures the number of linearly independent paths through a program's source code. The higher cyclomatic complexity a function has, the harder it is to test. If your function is a condition, you may need to test it in two different scenarios (paths): when the condi...

#8. Define variables as close as possible to where they are used

Define variables as close as possible to their first use. Prefer variables with the most local scope as possible. Inside a function body, at run time, a variable begins to exist once the code execution reaches the point where it is defined - not the point where it is only declared. (The Stack Overflow question  What is the difference between a definition and a declaration?  will help you understand the difference between a variable declaration and a variable definition. See the  Answer  by sbi.) It benefits the readability of your code that you keep functions short and well structured. To achieve this, It is a key factor that every concept is limited to the exact scope where it belongs to. Define, initialize and use each variable exactly where it is needed, not any earlier. By following this simple guideline you will write code which is more readable, contains less defects, and is easier to debug. Refactoring a function body is one of the most common ...

#7. Always initialize variables

Variables should be initialized with a known and meaningful value as soon as they are created. A variable should never begin to exist without having a known value. The most common sign I've found that someone is a novice C++ programmer are uninitialized variables. You look at a function body and you see the definitions of many uninitialized variables, usually grouped at the beginning of the function. Sometimes they are assigned a value later; sometimes they aren't used at all; and sometimes they are used without having been initialized, causing unpredictable behaviour. This is bad. Very bad. It hurts readability and it is error prone. The risk of using the value of an uninitialized variable is never worth taking. Don't do it. Always initialize variables. Bibliography [McConnell 2004] Steve McConnell:  Code Complete, 2nd Edition , Microsoft Press, 2004. This book discusses the initialization of variables in Section 10.3, "Guidelines for initializing variable...

#6. Ensure entry and exit conditions in loops

There are at least two questions you should always ask yourself while you're writing code for a loop - and ask yourself again after writing it. Here they are: Will the loop body be entered at least once? Will the loop ever be exited? If your loop is a do .. while , the first question has an obvious answer: yes. It will be entered at least once (as long as the execution point reaches it, of course). That's why you chose that structure in the first place. If it is a while or a for , you should pay more attention to that. Review the loop condition thoroughly and think about the possibility of it being false at the very beginning of the loop. In that case, the loop body would not be executed. Is that scenario correct your design, or is it something to avoid? If the latter is true, then you should write the specific code to handle that. About the second question, the loop will be exited whenever its condition is evaluated to false . The condition is evaluated once for each it...

Welcome to The Deep Blue C++

Image
During my years as a software engineer, I have learnt quite a lot of things - and I keep learning every day. I learn from practice, discussions with workmates, reading... I especially learn from mistakes: code that doesn't do what I expect, code that doesn't do anything at all, code that does it but with some occasional crash or two... If you're a programmer, you know what I'm talking about. What I've learnt has substantially changed the way I write code. In my first years as a programmer I tried to avoid mistakes, but I hadn't made enough of them yet to discover the best ways to prevent them. Now I know better. I know there are some things that bring you into trouble very quickly. Other things will silently wait for the worst moment - maybe months or years later, to come to the surface in the form of even more original problems. Disorganized code, uninitialized variables, pointer arithmetics, implicit casts, syntactic overloading, inheritance in which ...

#5. "for" loops should be simple and well-formed

for loops should be well-formed. This means: They should have a single loop counter, which shall be of integral type or an iterator. The loop counter should only be modified in the increment expression of the loop. The loop counter should be incremented or decremented by the same amount at each loop iteration. The loop counter should be accompanied in the loop condition, if anything, by boolean variables. The loop body should be a compound statement, delimited by brackets. The loop body should not contain labels which are the destination of goto  statements. The loop body should contain at most one break statement. Use continue with care, preferably in the beginning of the loop body, to exclude certain iterations from the action. A  for  loop is a practical, readable (once you get used to it) and terse construct, but you need to use it well. Because of its uncommon syntax, using it in ...

#4. Avoid fall-through in nonempty cases of "switch" statements

Each  case  label in a switch statement shall be followed by one of the following: Any number of statements, followed by a  break  instruction. Another case label. A default label. Do not write statements below a case label and without a break  instruction after them. It is confusing. A switch statement is a controlled jump. Every time the code execution encounters the keyword  switch , it will jump to a different place. The question is where. And the answer is: To the case which is equal to the value of the expression in the switch , if there is one; To the default  case, if there isn't one of the above and it exists; To the line after the closing bracket in the switch block, otherwise. In the first case, when the  break  instruction is reached, the execution flow will jump to the line after the closing bracket in the switch block. Or should I say "if"? The break  instruction is actually optional. You could omit ...

#3. The condition in an "if" statement should be a simple boolean expression with no side effects

The expression inside an  if statement should be boolean, simple, and have no side effects. In C++, every statement is an expression of a certain type. It may be an ordinary type, such as  bool ,  int or a user-defined type such as  Customer ), or a special type called  void which basically means "no type". In C++ the expression inside the pair of brackets will be understood as a boolean expression. Some people carry the habit from their C days and pass an as  int an argument to the  if . Other people even use a pointer, so that the  if will check whether it is NULL and will evaluate to true in that case. Don't do it - it makes the code less readable and a step closer to being error prone. Guideline #2 advised you to avoid implicit type conversions. This is a very good place to start applying it. The  if  statement requires a bool , so why pass it anything else? Don't overcomplicate things by putting several actions tog...

#2. Make all type conversions explicit

Assignments, function calls and expressions in general should not contain implicit type conversions. All type conversions should be explicit. Type conversions should be isolated in their own line, which should consist only of the assignment of the result of the type conversion to a variable of the target type. A statement should not mix type conversion with other operations. For example, if you need to convert an object to one of another type to pass it as a function parameter, do not do it in the function call itself; instead, do it in a separate instruction in the previous line. Type conversion occurs when an entity of type A is, well, converted to an entity of type B. This may occur in function calls, expressions using operators, or assignments. An explicit type conversion is one which you can read in the code. An implicit type conversion is one which does not appear in the source code, but is done automatically when the software is executed. Making all type conversi...

#1. Do one thing at a time

Do exactly one thing in each instruction, and put each of them in a separate line of code. This will enhance the readability of your code and make it easier to debug and possibly change in the future. It should not be possible for an instruction to have several potential causes of failure. If an instruction is a function call, its arguments should be trivial expressions. If its purpose is to get some result, don’t do anything complicated with it once you get it; instead, place the result in a variable and do subsequent operations with it in the following lines. If you write code in this way you will find out that it follows your train of thought quite closely. For this reason, a future reader (whether it is another programmer or you) will have a better chance of understanding it. Bibliography [McConnell 2004] Steve McConnell:  Code Complete, 2nd Edition , Microsoft Press, 2004. Chapter 31: "Layout and Style". Section 31.5: "Laying out individual statement...