Will a constructor be able to "call" a constructor in C++0x?

J

joe

The subject is the question. Furthering, doesn't it make sense to
consolidate code in a "main" constructor and have other ones use it to
provide different instantiation ways for a class? (rhetorical, I think,
because I think so). It's all about providing a rich class instantiation
interface to users (programmers) while having maintainable and reliable
source code, and allowing one constructor to "call" another (quotes used
because I just want the capability and am not too worried about the
syntax) gives exactly that.
 
B

Bo Persson

joe said:
The subject is the question. Furthering, doesn't it make sense to
consolidate code in a "main" constructor and have other ones use it
to provide different instantiation ways for a class? (rhetorical, I
think, because I think so). It's all about providing a rich class
instantiation interface to users (programmers) while having
maintainable and reliable source code, and allowing one constructor
to "call" another (quotes used because I just want the capability
and am not too worried about the syntax) gives exactly that.

Yes, it is called a "delegating constructor" when one constructor has
another constructor of the same class in its initializer list.

struct C
{
C( int ) { } // #1: non-delegating constructor
C(): C(42) { } // #2: delegates to #1
};



Bo Persson
 
H

Howard Hinnant

Yes, it is called a "delegating constructor" when one constructor has
another constructor of the same class in its initializer list.

struct C
{
   C( int ) { }   // #1: non-delegating constructor
   C(): C(42) { } // #2: delegates to #1

};

Imho, this is one of the more exciting features in C++0X, and not just
because it allows you to consolidate initialization code. I like it
for its ability to make writing exception safe code so much easier.

For example consider a class C which holds two resources (I'll just
use int pointers for ease of demonstration). Such a class in C++03
might be coded like this:

class C
{
int* data1_;
int* data2_;

public:
C() : data1_(0), data2_(0) {}

C(int i, int j)
: data1_(0), data2_(0) {}
{
try
{
data1_ = new int(i);
data2_ = new int(j);
}
catch (...)
{
delete data1_;
throw;
}
}

C(const C& c)
: data1_(0), data2_(0) {}
{
try
{
if (c.data1_)
data1_ = new int(*c.data1_);
if (c.data2_)
data2_ = new int(*c.data2_);
}
catch (...)
{
delete data1_;
throw;
}
}

~C()
{
delete data1_;
delete data2_;
}
}

Note the need to wrap the non-default constructors up with try-catch-
rethrow.

Delegating constructors have the property that once /any/ constructor
completes, the class is considered constructed, and thus its
destructor must run when the class goes out of scope. This means that
now the non-default constructors can rely on the default constructor
to construct the class, and after that, they can count on the
destructor to run if they throw an exception:

class C
{
int* data1_;
int* data2_;

public:
C() : data1_(nullptr), data2_(nullptr) {}

C(int i, int j)
: C()
{
data1_ = new int(i);
data2_ = new int(j);
}

C(const C& c)
: C()
{
if (c.data1_)
data1_ = new int(*c.data1_);
if (c.data2_)
data2_ = new int(*c.data2_);
}

~C()
{
delete data1_;
delete data2_;
}
}

Delegating constructors just really cleans up the code to deal with
exception safety, at least for those types that can delegate to a
resource-less constructor! :)

-Howard
 
S

Saeed Amrollahi

Imho, this is one of the more exciting features in C++0X, and not just
because it allows you to consolidate initialization code.  I like it
for its ability to make writing exception safe code so much easier.

For example consider a class C which holds two resources (I'll just
use int pointers for ease of demonstration).  Such a class in C++03
might be coded like this:

class C
{
    int* data1_;
    int* data2_;

public:
    C() : data1_(0), data2_(0) {}

    C(int i, int j)
        : data1_(0), data2_(0) {}
    {
        try
        {
            data1_ = new int(i);
            data2_ = new int(j);
        }
        catch (...)
        {
            delete data1_;
            throw;
        }
    }

    C(const C& c)
        : data1_(0), data2_(0) {}
    {
        try
        {
            if (c.data1_)
                data1_ = new int(*c.data1_);
            if (c.data2_)
                data2_ = new int(*c.data2_);
        }
        catch (...)
        {
            delete data1_;
            throw;
        }
    }

    ~C()
    {
        delete data1_;
        delete data2_;
    }

}

Note the need to wrap the non-default constructors up with try-catch-
rethrow.

Delegating constructors have the property that once /any/ constructor
completes, the class is considered constructed, and thus its
destructor must run when the class goes out of scope.  This means that
now the non-default constructors can rely on the default constructor
to construct the class, and after that, they can count on the
destructor to run if they throw an exception:

class C
{
    int* data1_;
    int* data2_;

public:
    C() : data1_(nullptr), data2_(nullptr) {}

    C(int i, int j)
        : C()
    {
        data1_ = new int(i);
        data2_ = new int(j);
    }

    C(const C& c)
        : C()
    {
        if (c.data1_)
            data1_ = new int(*c.data1_);
        if (c.data2_)
            data2_ = new int(*c.data2_);
    }

    ~C()
    {
        delete data1_;
        delete data2_;
    }

}

Delegating constructors just really cleans up the code to deal with
exception safety, at least for those types that can delegate to a
resource-less constructor! :)

-Howard

Hi Howard
Thank you for your answer. It is somehow different approach -Exception
safety & RAII-
to Delegating constructors.
Unfortunately, the two compilers I use, VC++ 2010 express edition and G
++ 4.5.0 still
don't support the feature.

Regards,
-- Saeed Amrollahi
 
P

Pavel

Howard said:
Imho, this is one of the more exciting features in C++0X, and not just
because it allows you to consolidate initialization code. I like it
for its ability to make writing exception safe code so much easier.

For example consider a class C which holds two resources (I'll just
use int pointers for ease of demonstration). Such a class in C++03
might be coded like this:

class C
{
int* data1_;
int* data2_;

public:
C() : data1_(0), data2_(0) {}

C(int i, int j)
: data1_(0), data2_(0) {}
{
try
{
data1_ = new int(i);
data2_ = new int(j);
}
catch (...)
{
delete data1_;
throw;
}
}

C(const C& c)
: data1_(0), data2_(0) {}
{
try
{
if (c.data1_)
data1_ = new int(*c.data1_);
if (c.data2_)
data2_ = new int(*c.data2_);
}
catch (...)
{
delete data1_;
throw;
}
}

~C()
{
delete data1_;
delete data2_;
}
}

Note the need to wrap the non-default constructors up with try-catch-
rethrow.

Delegating constructors have the property that once /any/ constructor
completes, the class is considered constructed, and thus its
destructor must run when the class goes out of scope. This means that
now the non-default constructors can rely on the default constructor
to construct the class, and after that, they can count on the
destructor to run if they throw an exception:

class C
{
int* data1_;
int* data2_;

public:
C() : data1_(nullptr), data2_(nullptr) {}

C(int i, int j)
: C()
{
data1_ = new int(i);
data2_ = new int(j);
}

C(const C& c)
: C()
{
if (c.data1_)
data1_ = new int(*c.data1_);
if (c.data2_)
data2_ = new int(*c.data2_);
}

~C()
{
delete data1_;
delete data2_;
}
}

Delegating constructors just really cleans up the code to deal with
exception safety, at least for those types that can delegate to a
resource-less constructor! :)

-Howard
This is certainly a useful technique that delegating constructors make
possible, but, just for the record: as usual there is no entirely free
cheese: the cost is that a member variable is written to more than once.

-Pavel
 
T

Thomas J. Gritzan

Am 23.08.2010 03:22, schrieb Pavel:
This is certainly a useful technique that delegating constructors make
possible, but, just for the record: as usual there is no entirely free
cheese: the cost is that a member variable is written to more than once.

Both are initialized to 0/NULL/nullptr, but does it matter when
allocating memory using new?
 
J

Joshua Maurice

Am 23.08.2010 03:22, schrieb Pavel:

Both are initialized to 0/NULL/nullptr, but does it matter when
allocating memory using new?

I would hope that static flow analysis with possible inlining would
easily remove any runtime penalty.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,267
Latest member
WaylonCogb

Latest Threads

Top