Nerd wannabe

lazy weblog

C++ and C++ TR1 (Technical Report 1)

with 5 comments

In this post I will assume you’re familiar with other languages like Java and C#, which are OO languages, and you start to learn the ‘multi-paradigm’ C++. Yeah, you couldn’t be that guy, could you.

So you already know the syntax.. so quick, let’s write classes!

We’ll start with a ‘bitmap’ that supposedly encapsulates a bitmap image, allocating memory at construction and freeing it at destruction:

/* bitmap.h */
#include <string>
class bitmap
{
    unsigned char *data;
public:
    bitmap(std::string path);
    ~bitmap(void);
};
/* bitmap.cpp */
#include "bitmap.h"
bitmap::bitmap(std::string path)
{
    //allocate memory
    data = new unsigned char [1024];
    //read data from path
    //parse it as an image
}
bitmap::~bitmap(void)
{
    //free the memory
    delete [] data;
}

Now that’s pretty simple and obvious, given that details about reading/parsing the bitmap are left out.Let’s see how we can use this class; we can allocate a bitmap object on heap like this:

/* main.cpp */
#include "bitmap.h"
int main (int argc, char * argv[])
{
    bitmap *icon = new bitmap ("./icon");
}

Oops.. not good, our destructor doesn’t get called. We have to manualy call delete icon so that it calls its delete [] data.

Hmm.. ugly.

But we do know that desctructors are automatically called when an auto variable gets out of scope and so we do this:

 bitmap icon = bitmap ("./icon");

Bummer! this time the destructor is called twice! Obviously, it fails the second time:(

The reason is that we weren’t aware that the default assignment operator is called in what we thought was an initializer (that ‘=’ that stands between the object and the constructor call).

This is actually one of the reasons most C++ code reads like:

bitmap icon("./icon");

The other is performance.
(yeah, event a simple int f = 0; gets obfuscated to int f(0); which looks like a goddamn function call)

Introducing auto_ptr<T>

We can further get rid of: the stars ‘*’ and the destructor by declaring the data to be an auto_ptr:

#include <string>
#include <memory>
using namespace std;
class bitmap
{
    auto_ptr<unsigned char> data;
public:
    bitmap(string path);
};
#include "bitmap.h"
bitmap::bitmap(string path)
{
    data = auto_ptr<unsigned char> (new unsigned char [1024]);
}

auto_ptr is a class that wraps a pointer. Its instance being used without dynamic allocation, the instances are freed (after being destructed) when they are out of scope.

This simply allows the ~auto_ptr () destructor to call ‘delete’ on the wrapped heap object just like with an auto object: when it gets out of scope.

It allows us to write code like this – once again:

 bitmap icon = bitmap ("./icon");

and like this:

 data = auto_ptr<unsigned char> (new unsigned char [1024]);

That is because the assignment operator of the auto_ptr transfers ownership of the wrapped object, NULL-ing the old reference, and because the right-hand expressions in the above two examples are not used further in the code.

You’re still fucked up if you write:

bitmap largeicon = icon;

since the right-hand object is left with a NULL reference!

Introducing shared_ptr<T>

shared_ptr is a class that boost provided long time ago, and now will be part of the upcoming C++ standard (C++09 probably, but currently named C++0x).

This is, one way or another, already supported in the upcoming Visual C++ 9 SP1 (today as a beta feature pack) and also in GNU g++ 4.x

The simple fact that this feature, now in the standard, can be provided by a library tells something about the power of C++. Unfortunately, the fact that the boost implementation is unreadable tells another thing – about C++ compilers. (to think about it, what other languages have as many compilers – and compilers’ problems – as C and C++?)

Basically, shared_ptr is counting the references to a pointer, and, when copied, increments the references, and when destructed, decrements their number. Finally, when no more references exists, it calls ‘delete’.

So our pointer-free source (sic!) becomes:

#include <string>
#include <memory>
using namespace std;
using namespace tr1;
class bitmap
{
    shared_ptr<unsigned char> data;
public:
    bitmap(string path);
};
#include "bitmap.h"
bitmap::bitmap(string path)
{
    data = shared_ptr<unsigned char> (new unsigned char [1024]);
}
#include "bitmap.h"
int main (int argc, char * argv[])
{
    bitmap icon = bitmap ("./icon");
    bitmap largeicon = icon;
}

If read up until here, you have just seen the way to aquire the memory resource on instantiation, so that you don’t need to manage it’s release.

TR1 has other new things too.. next time we’ll check on the <functional> classes function, bind and result_of.

(to compile the above in g++ 4.x, use #include <tr1/memory> and using namespace std::tr1;).

Advertisements

Written by vlad

March 12, 2008 at 3:32 pm

Posted in trivia

5 Responses

Subscribe to comments with RSS.

  1. your code only works with smart ptr’s cause of the aliasing between new/new[], delete/delete[] and your pod array

    most smart pointers are not meant for array’s, they break on any non-pod array cause delete only destructs the first object while delete[] destructs all objects

    consider shared_array from boost instead of misusing single-object smart-pointers for arrays

    Ronny Pfannschmidt

    March 25, 2008 at 10:57 am

  2. This is only a toy example where a ‘memory resource’ was represented as an unsigned char[]. It could have been a memory malloc’d in libpng for example.

    Of course, for ‘real’ code, boost is still needed, with or without tr1 (I suppose they are cooking a tr1-compatible version right now).

    vlad

    March 25, 2008 at 11:23 am

  3. The reason why your second example (“bitmap icon = bitmap (“./icon”);”) failed was that you didn’t define a copy constructor and an assignment operator. Generally, if you need to define a dtor (to free memory), you need to define those two as well:

    // copy ctor
    bitmap::bitmap(const bitmap& b)
    { memcpy(data, b.data, sizeof data); }

    // assignment operator
    bitmap& bitmap::operator=(const bitmap& b)
    { memcpy(data, b.data, sizeof data); return *this; }

    Mauro

    April 28, 2008 at 1:56 am

  4. Somehow i missed the point. Probably lost in translation :) Anyway … nice blog to visit.

    cheers, Distanced!

    Distanced

    June 19, 2008 at 12:09 pm

  5. Most compilers will elide the extra constructors in the initialization of a form A a = A(). This is permissible by the ISO standard (to eliminate a temporary when its only use is to initialize a variable of the same type)

    Testcase:
    #include
    class A {
    public:
    A(int) { std::cout << “A::A(int)” << std::endl; }
    A(const A&) { std:: cout << “A::A(const A&)” << std::endl; }
    A& operator=(const A&) { std::cout << “A::operator=(const A&)” << std::endl; }
    ~A() { std::cout << “A::~A()” << std::endl; }
    };

    int main() {
    A a = A(5);
    }

    On Linux, gcc:
    A::A(int)
    A::~A()

    On Linux, gcc, with -fno-elide-constructors:
    A::A(int)
    A::A(const A&)
    A::~A()
    A::~A()

    On Windows, MSVC 2008:
    A::A(int)
    A::~A()

    ASk

    August 23, 2008 at 9:03 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: