Destructors

We are at the end of the manual for writing a task, but we forgot to implement one important aspect of our analysis task: the destructor, copy constructor and assignment operator. Out class dynamically allocates memory (use of the new), for example when we create our histograms


        // create our histo and add it to the list
        fHistPt = new TH1F("fHistPt", "fHistPt", 100, 0, 100);

This has two implications:

  • After usage, we are responsible for freeing the allocated memory (else it’s lost!)

  • A bit more advanced: you might have to think about the copy constructor and assignment operator

Implementing the destructor

Memory allocated with new cannot be used again, unless it is realased by delete. Never deleting memory is called a memory leak. Leaks are bad: they waste resources and can crash your system, and for that reason, we should implement a destructor

Suppose we have a constructor that looks like this

    MyClass::MyClass()
    {
      n = new int;
      p = new float;
      x = new float[5];
    }

The destructor should be

    MyClass::~MyClass()
    {
      delete n;
      delete p;
      delete[] x;
    }

Our life is not so easy however:

  • We did not allocate memory in the class constructor but in UserCreateOutputObjects

  • We might want to free that memory, but what if UserCreateOutputObjects was not called? —if it hasn’t, freeing the memory will cause serious problems (the infamous ‘glibc double free or corruption’)

  • To avoid this, initialize our pointers to NULL in the member initialization list

         ...
        AliAnalysisTaskMyTask::AliAnalysisTaskMyTask() : AliAnalysisTaskSE(), 
            fAOD(0), fOutputList(0), fHistPt(0)
        {
            // ROOT IO constructor, don't allocate memory here!
        }
         ...
    
  • With our pointers initialized to NULL, we can go ahead and write our destructor
    AliAnalysisMyTask::~AliAnalysisMyTask()
    {
      if(fOutputList) delete fOutputList;
    }
  • A little trick we applied is

    fOutputList->SetOwner(kTRUE);
    

    which tells the destructor of TList* to delete all objects added to it

  • Note that fAOD is not deleted - this pointer points to memory that was not allocated by our task

new - delete

Rule of thumb: all calls to new should be accompanied by a call to delete somewhere in your code

Destructors and PROOF

If you run on PROOF, your analysis class is not the owner of its output list. A little trick to avoid segmentation violations when running on PROOF is to write

    AliAnalysisMyTask::~AliAnalysisMyTask()
    {
      if(AliAnalysisManager::GetAnalysisManager()->GetAnalysisType()
        != AliAnalysisManager::kProofAnalysis) delete fOutputList;
    }

This only deletes the output list if you’re not running PROOF.

Copy constructor and assignment operator

Sometimes in AliRoot code you see copy constructors and assignment operators (these two always go together). In principle, we don’t need them because our analysis class is never copied (apart from the streamer actions, which do not rely on these constructors). However, you might occasionally see compiler warnings such as

     warning: 'class AliAnalysisTaskMyTask' has pointer 
     data members but does not override 'AliAnalysisTaskMyTask(const
     AliAnalysisTaskMyTask&)' or 'operator=(const AliAnalysisTaskMyTask&)' 
     [-Weffc++]"

Generally, these can be squashed by defining prototypes

    private:
      // not implemented
      AliAnalysisTaskMyTask(const AliAnalysisTaskMyTask&); 
      AliAnalysisTaskMyTask& operator=(const AliAnalysisTaskMyTask&);

Defining these functions as private implicates that we do not have provide an implementation.

So what does this mean, what are copy constructors and assignment operators, why do I need them, and how can I get them? If you are interested in this, keep reading.

In a nutshell:

        MyObject A;      // initialization by default constructor
        MyObject B(A);   // initialization by copy constructor
        MyObject C = A;  // Also initialization by copy constructor
        B = C;           // assignment by copy assignment operator

The copy constructor and assignment operator are automatically generated, but if your class has pointer members, they need to be customized (this is not a ROOT feature, just c++!).

If we wanted to implement our own copy constructor for our class, here is what it would look like

    AliAnalysisMyTask::AliAnalysisMyTask
      (const AliAnalysisMyTask& other) : AliAnalysisTaskSE(other),
      fOutputList(NULL), fHistPt(NULL)
    {
      if(other.fOutputList) fOutputList = (TList*)other.fOutputList->Clone();
      if(other.fHistPt) fHistPt = new TH1F(*other.fHistPt);
    }
  • Operating on NULL pointers is not allowed and the code will crash if you attempt to do that

  • Operating on uninitialized pointers is undefined and much more dangerous

  • The TH1F and most ROOT histogram classes have public copy constructors, so we can use them

  • The TList copy constructor is private, so we need to make a Clone of it instead

The assignment operator is very similar to the copy constructor, but it has the additional requirements for checking for self-assignment and returning a value. In our case, it could look like

    AliAnalysisMyTask& AliAnalysisMyTask::operator=
      (const AliAnalysisMyTask& other)
    {
      if(&other == this) return *this;
      outputlist = NULL;
      fHistPt = NULL;
      AliAnalysisTaskSE::operator=(other);
      if(other.fOutputList) fOutputList = (TList*)other.fOutputList->Clone();
      if(other.fHistPt) fHistPt = new TProfile(*other.fHistPt);
      return *this;
    }

results matching ""

    No results matching ""