Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #7337 > unrolled thread

Virtual functions in constructors. Is it possible?

Started byAnonymous <anonymous@no.net>
First post2011-07-02 00:19 +0200
Last post2011-07-06 01:28 +0200
Articles 20 on this page of 22 — 5 participants

Back to article view | Back to comp.lang.c++


Contents

  Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-02 00:19 +0200
    Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-02 00:37 +0200
      Re: Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-02 01:19 +0200
        Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-02 08:50 +0200
          Re: Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-02 10:50 +0200
            Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-02 11:17 +0200
              Re: Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-02 11:25 +0200
                Re: Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-02 11:31 +0200
                Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-02 12:00 +0200
                Re: Virtual functions in constructors. Is it possible? Werner <werasm@gmail.com> - 2011-07-04 01:10 -0700
                  Re: Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-04 20:41 +0200
                    Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-04 21:35 +0200
                      Re: Virtual functions in constructors. Is it possible? Leo Equinox Gaspard <ekinox1995@gmail.com> - 2011-07-04 23:59 +0200
                        Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-05 11:09 +0200
                          Re: Virtual functions in constructors. Is it possible? Leo Equinox Gaspard <ekinox1995@gmail.com> - 2011-07-05 13:15 +0200
                            Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-05 14:51 +0200
                              Re: Virtual functions in constructors. Is it possible? Leo Equinox Gaspard <ekinox1995@gmail.com> - 2011-07-05 19:55 +0200
                                Re: Virtual functions in constructors. Is it possible? Anonymous <anonymous@no.net> - 2011-07-05 20:46 +0200
                                Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-05 21:23 +0200
                                Re: Virtual functions in constructors. Is it possible? Werner <werasm@gmail.com> - 2011-07-06 00:14 -0700
    Re: Virtual functions in constructors. Is it possible? Virchanza <virtual@lavabit.com> - 2011-07-05 14:58 -0700
      Re: Virtual functions in constructors. Is it possible? "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-06 01:28 +0200

Page 1 of 2  [1] 2  Next page →


#7337 — Virtual functions in constructors. Is it possible?

FromAnonymous <anonymous@no.net>
Date2011-07-02 00:19 +0200
SubjectVirtual functions in constructors. Is it possible?
Message-ID<iulh53$8t7$1@speranza.aioe.org>
Is it possible to call a virtual function as Init() in a constructor?

So that I could write something like this:

{
   Base a ;  // calls Base::Init
   Derived b ; // calls Derived::Init
}

Ok, before answering I must admit you that I have a solution, but I am 
just curious to know how many of you know the trick. When I saw it the 
first time, I was really surprised about the power of C++ :)

[toc] | [next] | [standalone]


#7338

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-02 00:37 +0200
Message-ID<iulhlm$tji$2@dont-email.me>
In reply to#7337
* Anonymous, on 02.07.2011 00:19:
> Is it possible to call a virtual function as Init() in a constructor?
>
> So that I could write something like this:
>
> {
> Base a ; // calls Base::Init
> Derived b ; // calls Derived::Init
> }
>
> Ok, before answering I must admit you that I have a solution, but I am just
> curious to know how many of you know the trick. When I saw it the first time, I
> was really surprised about the power of C++ :)

See the FAQ

<url: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6>


Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7339

FromAnonymous <anonymous@no.net>
Date2011-07-02 01:19 +0200
Message-ID<iulkmt$fvq$1@speranza.aioe.org>
In reply to#7338
> See the FAQ
> 
> <url: 
> http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6>

That is an ugly solution in my opinion. I think there is something much 
more elegant.. :)

[toc] | [prev] | [next] | [standalone]


#7351

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-02 08:50 +0200
Message-ID<iumejo$kv6$1@dont-email.me>
In reply to#7339
* Anonymous, on 02.07.2011 01:19:
>> See the FAQ
>>
>> <url: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6>
>
> That is an ugly solution in my opinion.

It's not "a" solution, it's a discussion of the possible ways in current C++.


> I think there is something much more elegant.. :)

Well, spit it out.


Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7364

FromAnonymous <anonymous@no.net>
Date2011-07-02 10:50 +0200
Message-ID<iumm5c$di7$1@speranza.aioe.org>
In reply to#7351
Alf P. Steinbach /Usenet ha scritto:
> * Anonymous, on 02.07.2011 01:19:
>>> See the FAQ
>>>
>>> <url: 
>>> http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6>
>>
>> That is an ugly solution in my opinion.
> 
> It's not "a" solution, it's a discussion of the possible ways in current 
> C++.

Ok, to be precise, the ways mentioned in the FAQ have the following 
limitations:

"In this variation, the code that is creating the object *explicitly 
executes both phases*. When executing Phase I, the code creating the 
object either knows the object's exact class (e.g., new Derived() or 
perhaps a local Derived object), or doesn't know the object's exact 
class (e.g., the virtual constructor idiom or some other factory). The 
"doesn't know" case is strongly preferred when you want to make it easy 
to plug-in new derived classes."

and


"The next approach does not rely on a two-phase initialization, instead 
using a second hierarchy whose only job is to house methods foo() and 
bar(). *This approach doesn't always work, and in particular it doesn't 
work in cases when foo() and bar() need to access the instance data 
declared in Derived*, but it is conceptually quite simple and clean and 
is commonly used."

In a way or the other static member functions are used there, which make 
the approaches just "ugly".

They key to the *perfect solution* is to profit by the lifetime of 
temporary objects passed to constructors: you can invoke a virtual 
function init() after an object has been constructed by calling it from 
a destructor of a temporary object passed to the constructor of the 
class that has to be initialized.

[toc] | [prev] | [next] | [standalone]


#7366

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-02 11:17 +0200
Message-ID<iumn7a$veh$1@dont-email.me>
In reply to#7364
* Anonymous, on 02.07.2011 10:50:
> Alf P. Steinbach /Usenet ha scritto:
>> * Anonymous, on 02.07.2011 01:19:
>>>> See the FAQ
>>>>
>>>> <url: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6>
>>>
>>> That is an ugly solution in my opinion.
>>
>> It's not "a" solution, it's a discussion of the possible ways in current C++.
>
> Ok, to be precise, the ways mentioned in the FAQ have the following limitations:
>
> "In this variation, the code that is creating the object *explicitly executes
> both phases*. When executing Phase I, the code creating the object either knows
> the object's exact class (e.g., new Derived() or perhaps a local Derived
> object), or doesn't know the object's exact class (e.g., the virtual constructor
> idiom or some other factory). The "doesn't know" case is strongly preferred when
> you want to make it easy to plug-in new derived classes."
>
> and
>
>
> "The next approach does not rely on a two-phase initialization, instead using a
> second hierarchy whose only job is to house methods foo() and bar(). *This
> approach doesn't always work, and in particular it doesn't work in cases when
> foo() and bar() need to access the instance data declared in Derived*, but it is
> conceptually quite simple and clean and is commonly used."
>
> In a way or the other static member functions are used there, which make the
> approaches just "ugly".
>
> They key to the *perfect solution* is to profit by the lifetime of temporary
> objects passed to constructors: you can invoke a virtual function init() after
> an object has been constructed by calling it from a destructor of a temporary
> object passed to the constructor of the class that has to be initialized.

So, if I do

   p = new YourClass()

and init() fails, do I have a memory leak or not?

But I think the scheme that you're suggesting should be in that FAQ item.

I didn't think of it when I convinced Marshall to add that FAQ item (and neither 
did he, or AFAIK anyone else), sorry.



Cheers & thanks,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7367

FromAnonymous <anonymous@no.net>
Date2011-07-02 11:25 +0200
Message-ID<iumo6l$hs6$1@speranza.aioe.org>
In reply to#7366
Alf P. Steinbach /Usenet ha scritto:
> * Anonymous, on 02.07.2011 10:50:
>> Alf P. Steinbach /Usenet ha scritto:
>>> * Anonymous, on 02.07.2011 01:19:
>>>>> See the FAQ
>>>>>
>>>>> <url: 
>>>>> http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6> 
>>>>>
>>>>
>>>> That is an ugly solution in my opinion.
>>>
>>> It's not "a" solution, it's a discussion of the possible ways in 
>>> current C++.
>>
>> Ok, to be precise, the ways mentioned in the FAQ have the following 
>> limitations:
>>
>> "In this variation, the code that is creating the object *explicitly 
>> executes
>> both phases*. When executing Phase I, the code creating the object 
>> either knows
>> the object's exact class (e.g., new Derived() or perhaps a local Derived
>> object), or doesn't know the object's exact class (e.g., the virtual 
>> constructor
>> idiom or some other factory). The "doesn't know" case is strongly 
>> preferred when
>> you want to make it easy to plug-in new derived classes."
>>
>> and
>>
>>
>> "The next approach does not rely on a two-phase initialization, 
>> instead using a
>> second hierarchy whose only job is to house methods foo() and bar(). 
>> *This
>> approach doesn't always work, and in particular it doesn't work in 
>> cases when
>> foo() and bar() need to access the instance data declared in Derived*, 
>> but it is
>> conceptually quite simple and clean and is commonly used."
>>
>> In a way or the other static member functions are used there, which 
>> make the
>> approaches just "ugly".
>>
>> They key to the *perfect solution* is to profit by the lifetime of 
>> temporary
>> objects passed to constructors: you can invoke a virtual function 
>> init() after
>> an object has been constructed by calling it from a destructor of a 
>> temporary
>> object passed to the constructor of the class that has to be initialized.
> 
> So, if I do
> 
>   p = new YourClass()
> 
> and init() fails, do I have a memory leak or not?

  no, because init() is called from a destructor of an object passed as 
argument to the constructor. From top down, if init() throws an 
exception, then the destruction fails, then the construction fails..

> 
> But I think the scheme that you're suggesting should be in that FAQ item.

where exactly?

[toc] | [prev] | [next] | [standalone]


#7368

FromAnonymous <anonymous@no.net>
Date2011-07-02 11:31 +0200
Message-ID<iumohe$hs6$2@speranza.aioe.org>
In reply to#7367
Anonymous ha scritto:
>> But I think the scheme that you're suggesting should be in that FAQ item.
> 
> where exactly?

sorry forget the question, i misread.

[toc] | [prev] | [next] | [standalone]


#7370

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-02 12:00 +0200
Message-ID<iumpmn$bd3$1@dont-email.me>
In reply to#7367
* Anonymous, on 02.07.2011 11:25:
> Alf P. Steinbach /Usenet ha scritto:
>>>
>>> They key to the *perfect solution* is to profit by the lifetime of temporary
>>> objects passed to constructors: you can invoke a virtual function init() after
>>> an object has been constructed by calling it from a destructor of a temporary
>>> object passed to the constructor of the class that has to be initialized.
>>
>> So, if I do
>>
>> p = new YourClass()
>>
>> and init() fails, do I have a memory leak or not?
>
> no, because init() is called from a destructor of an object passed as argument
> to the constructor. From top down, if init() throws an exception, then the
> destruction fails, then the construction fails..

I think you're mistaken.

Could you post a complete smallest possible example?


Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7431

FromWerner <werasm@gmail.com>
Date2011-07-04 01:10 -0700
Message-ID<e92f861a-e982-4dec-9f95-bf8286a15187@q1g2000vbj.googlegroups.com>
In reply to#7367
On Jul 2, 11:25 am, Anonymous <anonym...@no.net> wrote:


> no, because init() is called from a destructor of an object passed as
> argument to the constructor. From top down, if init() throws an
> exception, then the destruction fails, then the construction fails..

Init() may not fail by throwing, since it's called from a
destructor, and throwing would imply terminating (15.2.3).

Kind Regards,

Werner

[toc] | [prev] | [next] | [standalone]


#7447

FromAnonymous <anonymous@no.net>
Date2011-07-04 20:41 +0200
Message-ID<iut1gm$4dn$1@speranza.aioe.org>
In reply to#7431
Werner ha scritto:
> On Jul 2, 11:25 am, Anonymous <anonym...@no.net> wrote:
> 
> 
>> no, because init() is called from a destructor of an object passed as
>> argument to the constructor. From top down, if init() throws an
>> exception, then the destruction fails, then the construction fails..
> 
> Init() may not fail by throwing, since it's called from a
> destructor, and throwing would imply terminating (15.2.3).
> 
> Kind Regards,
> 
> Werner

Ok, after all I think that if necessary init() might handle failures 
inside its body.

class VirtualBase {
protected:
     class Initializer;
     friend class Initializer;

     class Initializer {
     public:

         Initializer() {
             std::cout << "Initializer" << std::endl;
             p = 0;
         }

         ~Initializer() {
             std::cout << "~Initializer" << std::endl;
             if (p)
                 p->Init(); // call the virtual function
         }
         // might be private if VirtualBase is declared as friend...

         void Register(VirtualBase* b) const {
             p = b;
         }
     private:
         mutable VirtualBase* p;
         // private and not implemented
         Initializer(const Initializer&);
         void operator =(const Initializer&);
     };
public:

     VirtualBase(const Initializer& i) {
         std::cout << "VirtualBase" << std::endl;
         i.Register(this);
     }
private:
     virtual void Init() = 0;
     // will be called immediately after the constructor
     // of the most derived class
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

     Base(const VirtualBase::Initializer& i = Initializer()) : 
VirtualBase(i) {
         std::cout << "Base" << std::endl;
     }

     ~Base() {
         std::cout << "~Base" << std::endl;
     }

     void Init() {
         std::cout << "Base::Init()" << std::endl;
     }
};

class Derived : public Base {
public:
     //  Derived() : Base() {} // compile-time error as wanted

     Derived(const VirtualBase::Initializer& i = Initializer()) :
     Base(i), /* Base(i) is optional...*/
     VirtualBase(i)
     {
         std::cout << "Derived" << std::endl;
     }

     ~Derived() {
         std::cout << "~Derived" << std::endl;
     }

     void Init() {
         std::cout << "Derived::Init()" << std::endl;
     }
};


int main()  {
   Base x;  // calls Base::Init
   Derived y ; // calls Derived::Init
   return( 0 ) ;
}

[toc] | [prev] | [next] | [standalone]


#7452

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-04 21:35 +0200
Message-ID<iut4g4$jio$1@dont-email.me>
In reply to#7447
* Anonymous, on 04.07.2011 20:41:
>
> class VirtualBase {
> protected:
>      class Initializer;
>      friend class Initializer;
>
>      class Initializer {
>      public:
>
>          Initializer() {
>              std::cout << "Initializer" << std::endl;
>              p = 0;
>          }
>
>          ~Initializer() {
>              std::cout << "~Initializer" << std::endl;
>              if (p)
>                  p->Init(); // call the virtual function
>          }
>          // might be private if VirtualBase is declared as friend...
>
>          void Register(VirtualBase* b) const {
>              p = b;
>          }
>      private:
>          mutable VirtualBase* p;
>          // private and not implemented
>          Initializer(const Initializer&);
>          void operator =(const Initializer&);
>      };
> public:
>
>      VirtualBase(const Initializer& i) {
>          std::cout << "VirtualBase" << std::endl;
>          i.Register(this);
>      }
> private:
>      virtual void Init() = 0;
>      // will be called immediately after the constructor
>      // of the most derived class
> };
>
> // This is the actual hierarchy
>
> class Base : public virtual VirtualBase {
> public:
>
>      Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
>          std::cout << "Base" << std::endl;
>      }
>
>      ~Base() {
>          std::cout << "~Base" << std::endl;
>      }
>
>      void Init() {
>          std::cout << "Base::Init()" << std::endl;
>      }
> };
>
> class Derived : public Base {
> public:
>      //  Derived() : Base() {} // compile-time error as wanted
>
>      Derived(const VirtualBase::Initializer& i = Initializer()) :
>      Base(i), /* Base(i) is optional...*/
>      VirtualBase(i)
>      {
>          std::cout << "Derived" << std::endl;
>      }
>
>      ~Derived() {
>          std::cout << "~Derived" << std::endl;
>      }
>
>      void Init() {
>          std::cout << "Derived::Init()" << std::endl;
>      }
> };
>
>
> int main()  {
>    Base x;  // calls Base::Init
>    Derived y ; // calls Derived::Init
>    return( 0 ) ;
> }

I reworked your example to show off the memory leak problem that I mentioned.

In addition this led me to realize one problem that I didn't think of earlier, 
namely that in the event of a failing constructor, the Initializer destructor 
will be calling a virtual method on a non-existent object, invoking UB.

Here's the reworked code, just play with the macro symbols NO_INIT_CALL, 
FAIL_CONSTRUCTOR and FAIL_INIT:


<code>
#include <iostream>
#include <stdexcept>
#include <stdlib.h>
using namespace std;

namespace g {
     int nObjectsAllocated  = 0;
}    // namespace g

class VirtualBase {
protected:
     class Initializer;
     friend class Initializer;

     class Initializer {
     public:

         Initializer() {
             cout << "Initializer" << endl;
             p = 0;
         }

         ~Initializer() {
             cout << "~Initializer" << endl;
             #ifndef NO_INIT_CALL
                 if (p)
                     p->Init(); // call the virtual function
             #endif
         }
         // might be private if VirtualBase is declared as friend...

         void Register(VirtualBase* b) const {
             p = b;
         }
     private:
         mutable VirtualBase* p;
         // private and not implemented
         Initializer(const Initializer&);
         void operator =(const Initializer&);
     };
public:

     VirtualBase(const Initializer& i) {
         cout << "VirtualBase" << endl;
         i.Register(this);
     }
private:
     virtual void Init() = 0;
     // will be called immediately after the constructor
     // of the most derived class
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

     static void* operator new( size_t size )
     {
         cout << "Allocation" << endl;
         ++g::nObjectsAllocated;
         return ::operator new( size );
     }

     static void operator delete( void* p )
     {
         cout << "Deallocation" << endl;
         --g::nObjectsAllocated;
         return ::operator delete( p );
     }

     Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
         cout << "Base" << endl;
         #ifdef FAIL_CONSTRUCTOR
             throw runtime_error( "Oops, `Base::Base` failed." );
         #endif
     }

     ~Base() {
         cout << "~Base" << endl;
     }

     void Init() {
         cout << "Base::Init()" << endl;
         #ifdef FAIL_INIT
             throw runtime_error( "Oops, `Init` failed." );
         #endif
     }
};

int main()
{
     int result = EXIT_FAILURE;
     try
     {
         Base* p = new Base;  // calls Base::Init
         cout << endl;
         delete p;
         result = EXIT_SUCCESS;
     }
     catch( exception const& x )
     {
         cerr << "!" << x.what() << endl;
     }

     if( g::nObjectsAllocated > 0 )
     {
         cerr << "!Memory leak." << endl;
     }
     cout << "Finished." << endl;
     return result;
}
</code>


As you can see the scheme, at least in its current incarnation, is not very 
exception-friendly: invoking UB, and leaking memory.

Can it be made exception safe?


Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7455

FromLeo Equinox Gaspard <ekinox1995@gmail.com>
Date2011-07-04 23:59 +0200
Message-ID<iutd2n$d9m$1@dont-email.me>
In reply to#7452
Le 04/07/2011 21:35, Alf P. Steinbach /Usenet a écrit :
> * Anonymous, on 04.07.2011 20:41:
>>
>> class VirtualBase {
>> protected:
>> class Initializer;
>> friend class Initializer;
>>
>> class Initializer {
>> public:
>>
>> Initializer() {
>> std::cout << "Initializer" << std::endl;
>> p = 0;
>> }
>>
>> ~Initializer() {
>> std::cout << "~Initializer" << std::endl;
>> if (p)
>> p->Init(); // call the virtual function
>> }
>> // might be private if VirtualBase is declared as friend...
>>
>> void Register(VirtualBase* b) const {
>> p = b;
>> }
>> private:
>> mutable VirtualBase* p;
>> // private and not implemented
>> Initializer(const Initializer&);
>> void operator =(const Initializer&);
>> };
>> public:
>>
>> VirtualBase(const Initializer& i) {
>> std::cout << "VirtualBase" << std::endl;
>> i.Register(this);
>> }
>> private:
>> virtual void Init() = 0;
>> // will be called immediately after the constructor
>> // of the most derived class
>> };
>>
>> // This is the actual hierarchy
>>
>> class Base : public virtual VirtualBase {
>> public:
>>
>> Base(const VirtualBase::Initializer& i = Initializer()) :
>> VirtualBase(i) {
>> std::cout << "Base" << std::endl;
>> }
>>
>> ~Base() {
>> std::cout << "~Base" << std::endl;
>> }
>>
>> void Init() {
>> std::cout << "Base::Init()" << std::endl;
>> }
>> };
>>
>> class Derived : public Base {
>> public:
>> // Derived() : Base() {} // compile-time error as wanted
>>
>> Derived(const VirtualBase::Initializer& i = Initializer()) :
>> Base(i), /* Base(i) is optional...*/
>> VirtualBase(i)
>> {
>> std::cout << "Derived" << std::endl;
>> }
>>
>> ~Derived() {
>> std::cout << "~Derived" << std::endl;
>> }
>>
>> void Init() {
>> std::cout << "Derived::Init()" << std::endl;
>> }
>> };
>>
>>
>> int main() {
>> Base x; // calls Base::Init
>> Derived y ; // calls Derived::Init
>> return( 0 ) ;
>> }
>
> I reworked your example to show off the memory leak problem that I
> mentioned.
>
> In addition this led me to realize one problem that I didn't think of
> earlier, namely that in the event of a failing constructor, the
> Initializer destructor will be calling a virtual method on a
> non-existent object, invoking UB.
>
> Here's the reworked code, just play with the macro symbols NO_INIT_CALL,
> FAIL_CONSTRUCTOR and FAIL_INIT:
>
>
> <code>
> #include <iostream>
> #include <stdexcept>
> #include <stdlib.h>
> using namespace std;
>
> namespace g {
> int nObjectsAllocated = 0;
> } // namespace g
>
> class VirtualBase {
> protected:
> class Initializer;
> friend class Initializer;
>
> class Initializer {
> public:
>
> Initializer() {
> cout << "Initializer" << endl;
> p = 0;
> }
>
> ~Initializer() {
> cout << "~Initializer" << endl;
> #ifndef NO_INIT_CALL
> if (p)
> p->Init(); // call the virtual function
> #endif
> }
> // might be private if VirtualBase is declared as friend...
>
> void Register(VirtualBase* b) const {
> p = b;
> }
> private:
> mutable VirtualBase* p;
> // private and not implemented
> Initializer(const Initializer&);
> void operator =(const Initializer&);
> };
> public:
>
> VirtualBase(const Initializer& i) {
> cout << "VirtualBase" << endl;
> i.Register(this);
> }
> private:
> virtual void Init() = 0;
> // will be called immediately after the constructor
> // of the most derived class
> };
>
> // This is the actual hierarchy
>
> class Base : public virtual VirtualBase {
> public:
>
> static void* operator new( size_t size )
> {
> cout << "Allocation" << endl;
> ++g::nObjectsAllocated;
> return ::operator new( size );
> }
>
> static void operator delete( void* p )
> {
> cout << "Deallocation" << endl;
> --g::nObjectsAllocated;
> return ::operator delete( p );
> }
>
> Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
> cout << "Base" << endl;
> #ifdef FAIL_CONSTRUCTOR
> throw runtime_error( "Oops, `Base::Base` failed." );
> #endif
> }
>
> ~Base() {
> cout << "~Base" << endl;
> }
>
> void Init() {
> cout << "Base::Init()" << endl;
> #ifdef FAIL_INIT
> throw runtime_error( "Oops, `Init` failed." );
> #endif
> }
> };
>
> int main()
> {
> int result = EXIT_FAILURE;
> try
> {
> Base* p = new Base; // calls Base::Init
> cout << endl;
> delete p;
> result = EXIT_SUCCESS;
> }
> catch( exception const& x )
> {
> cerr << "!" << x.what() << endl;
> }
>
> if( g::nObjectsAllocated > 0 )
> {
> cerr << "!Memory leak." << endl;
> }
> cout << "Finished." << endl;
> return result;
> }
> </code>
>
>
> As you can see the scheme, at least in its current incarnation, is not
> very exception-friendly: invoking UB, and leaking memory.
>
> Can it be made exception safe?
>
>
> Cheers & hth.,
>
> - Alf
>

Couldn't you use, in ~Initializer :

try {
   if (p)
     p->Init();
} catch(...) {
   p->InitFailed();
   throw;
}

And implement an InitFailed() virtual method that acts as the countrary 
(not sure of the word to use there) of Init ?

So the code should have no more memory leaks.

Cheers,
Leo

[toc] | [prev] | [next] | [standalone]


#7465

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-05 11:09 +0200
Message-ID<iuuk44$i31$1@dont-email.me>
In reply to#7455
* Leo Equinox Gaspard, on 04.07.2011 23:59:
>
> Couldn't you [one] use, in ~Initializer :
>
> try {
>    if (p)
>      p->Init();
> } catch(...) {
>    p->InitFailed();
>    throw;
> }

No.

This could handle exceptions from Init in the sense of reacting to them, at the 
cost of creating a zombie object (unusable object) when such an exception occurs.

But for this case it would still yield a memory leak, because this all happens 
after the new-expression has been fully evaluated. When the exception occurs in 
the constructor, the new-expression can clean up (and does). When the exception 
occurs after the new-expression is fully evaluated, which is the case with the 
call of Init, the new-expression is already out of the picture, in the past.

And it can't handle the case where the construction of the object that p is 
pointing to, throws an exception.

For then, by the time the ~Initializer runs (at the end of the full-expression 
that instantiates the object), the exception has propagated out of the 
new-expression and, due to the new-expression's cleanup, the object that p 
points to has been destroyed, hence, the call 'p->Init()' is now calling a 
virtual function on a non-existent object, which is Undefined Behavior. With g++ 
the error is caught by the runtime, and you get a crash. With MSVC it's not 
caught, and you get more subtle errors.


> And implement an InitFailed() virtual method that acts as the countrary (not
> sure of the word to use there) of Init ?
>
> So the code should have no more memory leaks.

Sorry, no, see above.


Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7467

FromLeo Equinox Gaspard <ekinox1995@gmail.com>
Date2011-07-05 13:15 +0200
Message-ID<iuurn9$sck$1@dont-email.me>
In reply to#7465
Le 05/07/2011 11:09, Alf P. Steinbach /Usenet a écrit :
> * Leo Equinox Gaspard, on 04.07.2011 23:59:
>>
>> Couldn't you [one] use, in ~Initializer :
>>
>> try {
>> if (p)
>> p->Init();
>> } catch(...) {
>> p->InitFailed();
>> throw;
>> }
>
> No.
>
> This could handle exceptions from Init in the sense of reacting to them,
> at the cost of creating a zombie object (unusable object) when such an
> exception occurs.
>
> But for this case it would still yield a memory leak, because this all
> happens after the new-expression has been fully evaluated. When the
> exception occurs in the constructor, the new-expression can clean up
> (and does). When the exception occurs after the new-expression is fully
> evaluated, which is the case with the call of Init, the new-expression
> is already out of the picture, in the past.
>
> And it can't handle the case where the construction of the object that p
> is pointing to, throws an exception.
>
> For then, by the time the ~Initializer runs (at the end of the
> full-expression that instantiates the object), the exception has
> propagated out of the new-expression and, due to the new-expression's
> cleanup, the object that p points to has been destroyed, hence, the call
> 'p->Init()' is now calling a virtual function on a non-existent object,
> which is Undefined Behavior. With g++ the error is caught by the
> runtime, and you get a crash. With MSVC it's not caught, and you get
> more subtle errors.
>

Oh. I thought the exception was in Init().
The run of the program I figured was :

=> new Derived()
  => Instantiation of an Initializer
  => Only do it, we don't want *any* exception in this constructor
=> End of new
=> ~Initializer()
  => Call to Init(), catching exceptions
   => Init() does the whole construction stuff, additionnally calling
      Base's Init()
  => If any exception was caught, then clear the work done by Init()
     using InitFailed()
   => InitFailed() should be exception-less and make the object ready to
      be destructed.
  => Then rethrow the exception
=> End of ~Initializer
=> If any exception is thrown, then the object is not valid, and hence 
should not be used, but immediately destructed. That's the default 
behavior of the exception, which will make any scoped items to be deleted.
Of course, using a raw pointer will lead to memory leaks, but that's 
always as it with them plus exceptions.

Where am I mistaking ?

Cheers,
Leo

>
>> And implement an InitFailed() virtual method that acts as the
>> countrary (not
>> sure of the word to use there) of Init ?
>>
>> So the code should have no more memory leaks.
>
> Sorry, no, see above.
>
>
> Cheers & hth.,
>
> - Alf
>

[toc] | [prev] | [next] | [standalone]


#7468

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-05 14:51 +0200
Message-ID<iuv17c$uog$1@dont-email.me>
In reply to#7467
* Leo Equinox Gaspard, on 05.07.2011 13:15:
> Le 05/07/2011 11:09, Alf P. Steinbach /Usenet a écrit :
>> * Leo Equinox Gaspard, on 04.07.2011 23:59:
>>>
>>> Couldn't you [one] use, in ~Initializer :
>>>
>>> try {
>>> if (p)
>>> p->Init();
>>> } catch(...) {
>>> p->InitFailed();
>>> throw;
>>> }
>>
>> No.
>>
>> This could handle exceptions from Init in the sense of reacting to them,
>> at the cost of creating a zombie object (unusable object) when such an
>> exception occurs.
>>
>> But for this case it would still yield a memory leak, because this all
>> happens after the new-expression has been fully evaluated. When the
>> exception occurs in the constructor, the new-expression can clean up
>> (and does). When the exception occurs after the new-expression is fully
>> evaluated, which is the case with the call of Init, the new-expression
>> is already out of the picture, in the past.
>>
>> And it can't handle the case where the construction of the object that p
>> is pointing to, throws an exception.
>>
>> For then, by the time the ~Initializer runs (at the end of the
>> full-expression that instantiates the object), the exception has
>> propagated out of the new-expression and, due to the new-expression's
>> cleanup, the object that p points to has been destroyed, hence, the call
>> 'p->Init()' is now calling a virtual function on a non-existent object,
>> which is Undefined Behavior. With g++ the error is caught by the
>> runtime, and you get a crash. With MSVC it's not caught, and you get
>> more subtle errors.
>>
>
> Oh. I thought the exception was in Init().
> The run of the program I figured was :
>
> => new Derived()
> => Instantiation of an Initializer
> => Only do it, we don't want *any* exception in this constructor
> => End of new
> => ~Initializer()
> => Call to Init(), catching exceptions
> => Init() does the whole construction stuff, additionnally calling
> Base's Init()
> => If any exception was caught, then clear the work done by Init()
> using InitFailed()
> => InitFailed() should be exception-less and make the object ready to
> be destructed.
> => Then rethrow the exception
> => End of ~Initializer
> => If any exception is thrown, then the object is not valid, and hence should
> not be used, but immediately destructed. That's the default behavior of the
> exception, which will make any scoped items to be deleted.
> Of course, using a raw pointer will lead to memory leaks, but that's always as
> it with them plus exceptions.
>
> Where am I mistaking ?

Instead of me explaining *again*, try out the given code. You can use compiler 
switch "-D" to define a macro symbol.

Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7482

FromLeo Equinox Gaspard <ekinox1995@gmail.com>
Date2011-07-05 19:55 +0200
Message-ID<iuvj59$s8m$1@dont-email.me>
In reply to#7468
Le 05/07/2011 14:51, Alf P. Steinbach /Usenet a écrit :
> * Leo Equinox Gaspard, on 05.07.2011 13:15:
>> Oh. I thought the exception was in Init().
>> The run of the program I figured was :
>>
>> => new Derived()
>> => Instantiation of an Initializer
>> => Only do it, we don't want *any* exception in this constructor
>> => End of new
>> => ~Initializer()
>> => Call to Init(), catching exceptions
>> => Init() does the whole construction stuff, additionnally calling
>> Base's Init()
>> => If any exception was caught, then clear the work done by Init()
>> using InitFailed()
>> => InitFailed() should be exception-less and make the object ready to
>> be destructed.
>> => Then rethrow the exception
>> => End of ~Initializer
>> => If any exception is thrown, then the object is not valid, and hence
>> should
>> not be used, but immediately destructed. That's the default behavior
>> of the
>> exception, which will make any scoped items to be deleted.
>> Of course, using a raw pointer will lead to memory leaks, but that's
>> always as
>> it with them plus exceptions.
>>
>> Where am I mistaking ?
>
> Instead of me explaining *again*, try out the given code. You can use
> compiler switch "-D" to define a macro symbol.
>
> Cheers & hth.,
>
> - Alf
>

I *did* try it.
And, as soon as I change it to reflect what I mean - this means removing the
FAIL_CONSTRUCTOR case and adding a InitFailed() -, everything works OK. BTW
the NO_INIT_CALL looks useless to me, so I removed it.
And, for the -D switch, thanks, but I knew it.
Please also note that InitFailed doesn't has any use, but it could be 
used in
case of raw pointer manipulation.
The code (you only have to play with FAIL_INIT) :
<code>
#include <iostream>
#include <stdexcept>
#include <cstdlib>
#include <memory>
using namespace std;

namespace g {
     int nObjectsAllocated  = 0;
}    // namespace g

class VirtualBase {
protected:
     class Initializer;
     friend class Initializer;

     class Initializer {
     public:

         Initializer() {
             cout << "Initializer" << endl;
             p = 0;
         }

         ~Initializer() {
             cout << "~Initializer" << endl;
             try {
                 if (p)
                     p->Init(); // call the virtual function
             } catch (...) {
                 p->InitFailed();
                 throw;
             }
         }
         // might be private if VirtualBase is declared as friend...

         void Register(VirtualBase* b) const {
             p = b;
         }
     private:
         mutable VirtualBase* p;
         // private and not implemented
         Initializer(const Initializer&);
         void operator =(const Initializer&);
     };
public:

     VirtualBase(const Initializer& i) {
         cout << "VirtualBase" << endl;
         i.Register(this);
     }
private:
     virtual void Init() = 0;
     // will be called immediately after the constructor
     // of the most derived class

     virtual void InitFailed() = 0;
     // will be called if Init failed. Should leave the object
     // in a destructable state.
};

// This is the actual hierarchy

class Base : public virtual VirtualBase {
public:

     static void* operator new( size_t size )
     {
         cout << "Allocation" << endl;
         ++g::nObjectsAllocated;
         return ::operator new( size );
     }

     static void operator delete( void* p )
     {
         cout << "Deallocation" << endl;
         --g::nObjectsAllocated;
         return ::operator delete( p );
     }

     Base(const VirtualBase::Initializer& i = Initializer()) : 
VirtualBase(i) {
         cout << "Base" << endl;
     }

     ~Base() {
         cout << "~Base" << endl;
     }

     void Init() {
         cout << "Base::Init()" << endl;
         #ifdef FAIL_INIT
             throw runtime_error( "Oops, `Init` failed." );
         #endif
     }

     void InitFailed() {
         cout << "Base::InitFailed() - could do something if needed" << 
endl;
     }
};

int main()
{
     int result = EXIT_FAILURE;
     try
     {
         auto_ptr<Base> p(new Base);  // calls Base::Init.
         // We *need* an auto_ptr to ensure that the item will be
         // deleted in case of exception.
         result = EXIT_SUCCESS;
     }
     catch( exception const& x )
     {
         cerr << "!" << x.what() << endl;
     }

     if( g::nObjectsAllocated > 0 )
     {
         cerr << "!Memory leak." << endl;
     }
     cout << "Finished." << endl;
     return result;
}
</code>

So I must repeat my question : Where am I mistaking ?

Cheers,
Leo

[toc] | [prev] | [next] | [standalone]


#7485

FromAnonymous <anonymous@no.net>
Date2011-07-05 20:46 +0200
Message-ID<iuvm6d$85n$2@speranza.aioe.org>
In reply to#7482
Leo Equinox Gaspard ha scritto:
> I *did* try it.
> And, as soon as I change it to reflect what I mean - this means removing 
> the
> FAIL_CONSTRUCTOR case and adding a InitFailed() -, everything works OK. BTW
> the NO_INIT_CALL looks useless to me, so I removed it.
> And, for the -D switch, thanks, but I knew it.
> Please also note that InitFailed doesn't has any use, but it could be 
> used in
> case of raw pointer manipulation.
> The code (you only have to play with FAIL_INIT) :

This is what I meant in my previous post. IMHO it should be ok.

[toc] | [prev] | [next] | [standalone]


#7488

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-05 21:23 +0200
Message-ID<iuvo43$bb$1@dont-email.me>
In reply to#7482
* Leo Equinox Gaspard, on 05.07.2011 19:55:
> <code>
> #include <iostream>
> #include <stdexcept>
> #include <cstdlib>
> #include <memory>
> using namespace std;
>
> namespace g {
>      int nObjectsAllocated  = 0;
> }    // namespace g
>
> class VirtualBase {
> protected:
>      class Initializer;
>      friend class Initializer;
>
>      class Initializer {
>      public:
>
>          Initializer() {
>              cout << "Initializer" << endl;
>              p = 0;
>          }
>
>          ~Initializer() {
>              cout << "~Initializer" << endl;
>              try {
>                  if (p)
>                      p->Init(); // call the virtual function
>              } catch (...) {
>                  p->InitFailed();
>                  throw;
>              }
>          }
>          // might be private if VirtualBase is declared as friend...
>
>          void Register(VirtualBase* b) const {
>              p = b;
>          }
>      private:
>          mutable VirtualBase* p;
>          // private and not implemented
>          Initializer(const Initializer&);
>          void operator =(const Initializer&);
>      };
> public:
>
>      VirtualBase(const Initializer& i) {
>          cout << "VirtualBase" << endl;
>          i.Register(this);
>      }
> private:
>      virtual void Init() = 0;
>      // will be called immediately after the constructor
>      // of the most derived class
>
>      virtual void InitFailed() = 0;
>      // will be called if Init failed. Should leave the object
>      // in a destructable state.
> };
>
> // This is the actual hierarchy
>
> class Base : public virtual VirtualBase {
> public:
>
>      static void* operator new( size_t size )
>      {
>          cout << "Allocation" << endl;
>          ++g::nObjectsAllocated;
>          return ::operator new( size );
>      }
>
>      static void operator delete( void* p )
>      {
>          cout << "Deallocation" << endl;
>          --g::nObjectsAllocated;
>          return ::operator delete( p );
>      }
>
>      Base(const VirtualBase::Initializer& i = Initializer()) : VirtualBase(i) {
>          cout << "Base" << endl;
>      }
>
>      ~Base() {
>          cout << "~Base" << endl;
>      }
>
>      void Init() {
>          cout << "Base::Init()" << endl;
>          #ifdef FAIL_INIT
>              throw runtime_error( "Oops, `Init` failed." );
>          #endif
>      }
>
>      void InitFailed() {
>          cout << "Base::InitFailed() - could do something if needed" << endl;
>      }
> };
>
> int main()
> {
>      int result = EXIT_FAILURE;
>      try
>      {
>          auto_ptr<Base> p(new Base);  // calls Base::Init.
>          // We *need* an auto_ptr to ensure that the item will be
>          // deleted in case of exception.
>          result = EXIT_SUCCESS;
>      }
>      catch( exception const& x )
>      {
>          cerr << "!" << x.what() << endl;
>      }
>
>      if( g::nObjectsAllocated > 0 )
>      {
>          cerr << "!Memory leak." << endl;
>      }
>      cout << "Finished." << endl;
>      return result;
> }
> </code>
>
> So I must repeat my question : Where am I mistaking ?

Your code

* has Undefined Behavior if a constructor throws, and
* leaks memory without the auto_ptr

(while there's no leak for an ordinary single stage construction, because the 
new-expression then cleans up).

The UB is critical, so this technique is only applicable for guaranteed no-throw 
constructors, which except for default constructors are pretty rare beasts nowadays.

I don't know what you think you're mistaken about, sorry, but hopefully it's one 
of the two points above?


Cheers & hth.,

- Alf


-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7513

FromWerner <werasm@gmail.com>
Date2011-07-06 00:14 -0700
Message-ID<fa4a7df4-f2c4-4fb4-802a-aeb29654bd42@c41g2000yqm.googlegroups.com>
In reply to#7482
On Jul 5, 7:55 pm, Leo Equinox Gaspard <ekinox1...@gmail.com> wrote:

> I *did* try it.
> And, as soon as I change it to reflect what I mean - this means removing the
> FAIL_CONSTRUCTOR case and adding a InitFailed() -, everything works OK. BTW
> the NO_INIT_CALL looks useless to me, so I removed it.

Unfortunately you cannot easily impose this requirement (that
their constructors may not throw) on derived classes (the
client). This makes this idiom very restrictive.

Furthermore, throwing from within init, even when calling InitFailed
still causes the leak (even when using an auto_ptr).

Output using your addition (courtesy of codepad, my
comments added):

//Options used...
//#define FAIL_CONSTRUCTOR
#define FAIL_INIT
//#define NO_INIT_CALL


1) Initializer  //Parameter initialization from default
2) Allocation   //new called.
3) VirtualBase  //vbase constructor
4) Base         //base constructor
5) ~Initializer //Parameter out of scope...
6) Base::Init() //Valid virtual call
7) Base::InitFailed() //ditto
8) !Oops, `Init` failed. //final catch, but
  // where is Deallocation? This despite the
  // fact that we used an autoptr (Oops, auto_ptr
  // was never initialized, as new never completed...
9) !Memory leak. //... is still there, apart from the
  //UB if we define FAIL_CONSTRUCTOR...

10) Finished.

Kind regards,

Werner

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c++


csiph-web