Showing posts with label destructor. Show all posts
Showing posts with label destructor. Show all posts

2011-05-02

Unexpected behavior: pointer to constant

Preface: some words about correct 'const' syntax

The general rule is that the const keyword affects its immediate left neighbor. Sic! So the correct syntax for defining a pointer to a constant integer is int const * pInteger. The history proved that the traditional style is not sufficient, but is still maintained: If the const keyword is the leftmost in a type specification then it affects its immediate right neighbor.

Constants are to be disposed too

#include <iostream>
class A {
public:
        A(){ std::cout << "Risen #" << (idx=++counter) << std::endl; }
        ~A(){ std::cout << "Fallen #" << idx << std::endl; }
private:
        int idx;
        static int counter;
};
int A::counter = 0;

int main(){
        A const first;
        return 0;
}

will output (as expected)

Risen #1
Fallen #1

As we can observe, the destructor of the const-qualified first variable was called. That extrapolates that the const qualifier does not protect from calling the destructor on a pointer which is a weird looking thing for the first time.

The unexpected behavior

#include <iostream>
class A { /* same as the above */ };
int A::counter = 0;
void f(A const * pTest){ delete pTest; }

int main(){
        A *pFirst = new A, second;
        f(pFirst);
        A const third;
        return 0;
}

will compile flawlessly and will produce

Risen #1
Risen #2
Fallen #1
Risen #3
Fallen #3
Fallen #2

The bad idea

Let's make it impossible to call a delete on pointers to constants! Bad idea. The following example must be possible.

Vector2D const * pVector = new Vector2D(3,2);
// ...
delete pVector; // <--- This is it.

And the deletion of a pointed value must be delegable to functions. That's why the f() was able to delete its parameter.

One may feel unsafe at this moment, but it is not really a problem in the industry: almost nobody has the clue that it is even possible, and nobody wants to do that anyways, except when it is the main goal. But then it's not a trap.

2010-05-03

C++: Why initializer list?

class Parent;

class Child {
public:
    Child(
        Parent& parent_
        , const std::string& name_
        , const Color& eyeColor_
    )
    : parent(parent_)
    , name(name_)
    , eyeColor(eyeColor_)
    , money(0,"EUR")
    { }
    Parent& GetParent(){ return parent; }
    std::string GetName() const { return name; }
    Money GetMoney() const { return money; );
    Money AdjustMoney(int diff)
    { money += diff; return money; }
private:
    Parent &parent;
    const std::string name;
    Color eyeColor;
    Money money;
};
The initializer list in the above is this:
: parent(parent_)
, name(name_)
, eyeColor(eyeColor_)
, money(0,"EUR")
  • because of initializing references.

    References are like pointers with two restrictions. Firstly: You (if you are sane) point only to one object with a reference, you never point to an array. Secondly: Once the location is pointed you will not point elsewhere. This "finally" sounding behavior is perfectly good when we want to point one's (abstract) parent.

    If you want the language to force you to keep the second rule and still want your compiler in this millennium then you have to realize, that the initializer must be done before the begin ({) sign appears with it's endless possibilities.

  • because of initializing const members.

    Same as the above, but we are not talking about making up our mind early about a location but about a value. See the name member

  • because of initializing an aggregated object which do not have default constructor.

    Let's imagine the Color class which has only the copy constructor and that one: Color::Color(unsigned char r, unsigned char g, unsigned char b)! This class does not have default value / state by design, so it cannot be constructed without some instructions. See the eyeColor member.

  • because of speed

    You begin your program (constructor body) at the '{' sign. At that point all of your variables must be in a valid state. So if you give value to your members after that point you will waste processor time. One construction and one assignment, instead of simply one construction. See the money member.

    In most of my codes the constructors' body used to be empty. If we want to work in that style we will have to have handy constructors for our classes, which is not a bad thing at all.

Important: The execution of the initializer list is in the order of the data members because that order means the construction order and the reverse of that means the destruction order. If it wouldn't be like that then the program would have to remember the construction order and apply the reverse of it upon destruction. That would waste resources. So the rule came...

... and don't worry about hidden errors! Turn on the warnings in your compiler and take the advices and notes seriously! Seriously like Treat warnings as errors compiler option.