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. I will try to explain how to use C++ more effectively.

 

Actually, it would be more accurate to say that C++ supports OOP. In fact, mentioning the concept of a federation of languages at this point will not be 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 symbolic name PI may never be noticed by the compilers, or it can be deleted by the precompilers 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 will make debugging very hard, especially 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 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. This is truly 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 will 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;
    public:
    int getRaw () const {return raw;}
};
 

getRaw is a member function. If you pay attention, there is a “const” statement after the parentheses. This means that the member function (getRaw) will not change the value of any member variable (raw or foo). So when we write a sentence like “foo = 2” in getRaw, the compiler will give 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 on the very basis says, finish what you have started. The finalization should also be done if there is 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 memory leak. I will 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 changes to auto_ptr, and the pointer losing ownership becomes null/disabled. The new auto_ptr will disable 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 but when there is no shared_ptr left trying to access the resource, shared_ptr dies.

 

Examples:

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 will allow us to use the resources correctly.


Author: Kartaca

%d bloggers like this: