Thursday, 31 October 2013

RAII is a must know technique for c++ programmers, no excuse.

    RAII(resource acquisition is initialization), in a nutshell, is same as "let the object handle the resource".I use the word resource but not memory, because RAII can guard more than memory(mutex, db connnection, socket, file and every resource you can think of).There are many blogs, famous technical forum, gurus suggestion, mention and emphasize how important and what RAII is.

    C++ is a big, complexity language, I have no idea I need to take how many years to learn every corners of this powerful beast, but we do not need to learn all of it. We only need to know the most essential part of c++, RAII is one of them.You may not use any TMP(template meta programming) , multi inheritance(please think carefully before you use this feature), design any allocators, don't know what is policy based design, have no idea how type_traits work, do not familiar with operator overloading, but you will always find out that RAII could help you write good codes, make your codes become much more easier to maintain, read and more robust.

   If you don't believe it, let us look at a simple example.


#include <iostream>
#include <new>

int main()
{
    int *arr = nullptr;
    int *max = nullptr;
    double *double_arr = nullptr;

    try{
    arr = new int[100];
    max = new int(300);
    double_arr = new double[300];

    //.....do something......

    //handle your garbages if something happen
    goto cleanUp;
    //......
  }catch(std::bad_alloc const &ex){
    std::cerr<<ex.what()<<std::endl;
  }

  cleanUp:
    if(arr){
      delete []arr;
      arr = nullptr;
    }
    if(max){
      delete max;
      max = nullptr;
    }
    if(double_arr){
      delete []double_arr;
      double_arr = nullptr;
    }
}

    What is the problem with this example?They are perfectly legal and safe in C++?If you don't get it, let me ask you a problem.

Q : What if we need to handle more resource?

Naive answer(bad practice) : add more if else

The suck codes proposed by the naive answer may look like


#include <iostream>
#include <new>

int main()
{
    int *arr = nullptr;
    int *max = nullptr;
    double *double_arr = nullptr;
    float *float_max = nullptr;
    float *float_arr = nullptr;

    try{
        arr = new int[100];
        max = new int(300);
        double_arr = new double[300];
        float_max = new float(3.14);
        float_arr = new float[300];

        //.....do something......

        //handle your garbages if something happen
        goto cleanUp;
        //......
    }catch(std::bad_alloc const &ex){
        std::cerr<<ex.what()<<std::endl;
    }

cleanUp:
    if(arr){
        delete []arr;
        arr = nullptr;
    }
    if(max){
        delete max;
        max = nullptr;
    }
    if(double_arr){
        delete []double_arr;
        double_arr = nullptr;
    }
    if(float_max){
        delete float_max;
        float_max = nullptr;
    }
    if(float_arr){
        delete []float_arr;
        float_arr = nullptr;
    }
}

   You will have to alter two places whenever you need to add or delete the resources.This kind of c style codes are error prone, hard to maintain and unbearable, they are almost as worse as(even worse than it if we have to take care of exception) the old school c codes which heavily depends on goto to clean up resources, this kind of codes will become unreadable in short times. I believe this is one of the reason why Bjarne said "Aim to write idiomatic C++: avoid simply writing code in the style of your previous language using C++ syntax; there is little to be gained from simply changing syntax.".For any C++ programmers who know how important and powerful RAII is, they can't bear this kind of horrible codes.

    How could RAII help us?


#include <iostream>
#include <memory>
#include <new>
#include <vector>

int main()
{
  try{
   std::vector<int> arr(100);
   std::unique_ptr<int> max(new int(300));
   std::vector<double> double_arr(300);
   std::unique_ptr<double> double_max(new double(300));
   std::vector<float> float_arr(100);
   std::unique_ptr<float> float_max(new float(100));

   //...do something....
   
   //the object will release the resources automatically
   //even the exceptions thrown

  }catch(std::bad_alloc const &ex){
    std::cerr<<ex.what()<<std::endl;
  }
}
   

    Do you see that?The codes become much more easier to read, because we don't need to release the resource manually, we don't need to change the codes in two places.The codes become more robust, because we will not forget to release the resources again.More than that, it is exception safe too.c++ is good because you can deal with pointer and memory management, in c you have to deal with pointer and memory management.

    There are many kinds of coding standards, rules in many teams, not all coding standards suit for all teams and all applications.Whatever it is, RAII is a must know technique and should be obey when you can. If you are using delete in your codes, maybe you are doing something wrong or writing worse codes in C++, even you need to use delete(building some infrastructures, like smart pointer, containers and so on), do remember to protect it by object, always remember to follow the rule of RAII.Pointer is good at access data, but not resource management, leave your resource to class(RAII).

    In my humble opinion, if a C++ programmer don't know the concepts of RAII, he/she will generate lower quality, worse performance, less reliable codes most of the times(just like the suck codes show by the examples).  It is always a good question to test your interviewers, ask them they notice how important RAII in C++ or not.If they don't know it, don't hire them(or teach them if you think the candidates are worth to).If you were an interviewer and find out the company don't take RAII seriously, then you should be careful.