Products Solutions Contact TR

Developing Effective Code with C++


I like C++. If you would like to learn object-oriented programming and know what you program, I would recommend C++. There is a lot you can do with C++, and it would be even better if you do it effectively. In this article, I explain how to use C++ more effectively.


It would be more accurate to say that C++ supports OOP, and mentioning the concept of a federation of languages at this point is not wrong.

C++ is a language that supports the coexistence of many paradigms. It is a multiparadigm language, supporting procedural, functional, generic, metaprogramming, and object-oriented programming paradigms. We can explain these concepts in another blog post. As this is our first evaluation, let’s start with the first rule:  

1- We need to know which type of paradigm we are moving forward with to develop effective C++ code.


When developing the C++ code, we should prefer the compiler to the precompiler. What does this mean? We should use const, enums, and inline keywords instead of define:


#define PI 3.1416


The compilers may never notice the symbolic name PI or the precompilers can delete it before it even reaches the compiler. As a result, the PI may never join the symbol table. When “define” is used, the compiler may never find the error related to PI, and this makes debugging very hard, exceptionally if “define” is in a header file we didn’t write.


The solution to this is straightforward; we need to replace the sentence as below:


const double Pi = 3.1416


Then our rule is;


2- We should always choose the compiler over the precompiler.


We see constant “const” in C++ codes. Frankly, it used to make me a little uncomfortable because I was used to writing loosely-coupled code. Of course, this was causing errors all the time. You should always use “const” if you don’t want to change a variable. It is genuinely an encapsulation process.


I don’t want to go in detail about what “const” is, but let’s check a good example applied to operators:

class Number {....};

const Number operator* (const Number& lhs, const Number& rhs);

So, that must be our code. Our code does not allow such an attempt:

Number a,b,c; 
(a * b) = c;

If the result of the operator* wasn’t a “const” number, this process could be successful, but in this case, we are preventing that error.


Let’s look at our example that is much simpler and shows encapsulation clearly:

class Number {
    int raw; 
    int foo;
    int getRaw () const {return raw;}

getRaw is a member function. If you pay attention, there is a “const” statement after the parentheses. It means that the member function (getRaw) does not change the value of any member variable (raw or foo). So when we write a sentence like “foo = 2” in getRaw, the compiler gives an error.


3- Const limits us; we should use “const” everywhere necessary.


One of my favorite aspects of C++ is that you get to decide how to use the resources. However, this leads to other problems that result in some languages gaining popularity over the others — Garbage Collector.


Maybe you’ve heard of a concept called “RAII“(Resource Acquisition Is Initialization), which very basically says, finish what you have started. The finalization should also be done if there is an initialization.


When an object is created, the constructor is called, and when it is killed, the destructor is called. If it is dying, then there is no problem. But there are some places in heap management that we should stop and look again. For example, if a pointer is created with an object, the pointer should especially be deleted when the destructor is called. Otherwise, we’re inviting the problem called a memory leak. I explain in detail how to create our RAII classes in another blog post. I’m going to talk about a few of the fastest resource management tools available.


If you don’t want to worry about whether the pointer is dead or not, using std: shared_ptr or auto_ptr is the best option you have. The difference between the two is that when you use the auto_ptr, the ownership of a pointer change to auto_ptr, and the pointer losing ownership becomes null/disabled. The new auto_ptr disables the previous auto_ptr if it is owned by another auto_ptr. It goes on like this. shared_ptr allows multiple pointers to access the same source; however, when there is no shared_ptr left trying to access the resource, shared_ptr dies.



std::auto_ptr<std::string> string1 (new std::string("there is a string here")); //points to the object starting with string1 new.. 

std::auto_ptr<std::string> string2 (string1); //string2 now points to string "there is a string here" but pay attention, string1 is now null

string1 = string2; // now points to string1 point again and string2 is null

Above, we see how auto_ptr works, and let’s look at shared_ptr:

std::shared_ptr<std::string> string1 (new string("there is a string here again")); // string1 points to the string "burada yine bir string var".

std::shared_ptr<std::string> string2 (string1); //both string1 and string2 pointers point to string "there is a string here again".

string1 = string2; // :) no change. everything is the same.
} // string1 and string2 pointers are destroyed and the object they are pointing is automatically deleted.

4- Using shared_ptr or auto_ptr based on our purpose allows us to use the resources correctly.

Author: Kartaca