Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > constructor and reset method

Reply
Thread Tools

constructor and reset method

 
 
jason.cipriani@gmail.com
Guest
Posts: n/a
 
      02-27-2008
On Feb 27, 12:48 am, Jeff Schwab <(E-Mail Removed)> wrote:
> For example: If B has a std::vector<int> member, the vector likely has
> a pointer to some dynamically allocated memory. By using placement new,
> rather than emptying the vector through its intended interface, you may
> be overwriting the vector's pointer to its dynamically allocated memory,
> such that the memory is leaked.


Hmm... but in this case, wouldn't calling this->~B() also invoke the
std::vector<int> member's destructor; which would free any memory the
vector had allocated? The memory used for the vector<int> itself would
stick around, of course (it's been destructed but not deallocated),
but any cleanup it did in it's destructor would happen. At least,
that's what I expect; because you can use placement new on object
types that have other objects as members, and the only thing you need
to do to clean up (aside from actually flagging the memory you used
for it as free in whatever way is appropriate) is call the object's
destructor.

But...

On Feb 27, 2:04 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> In particular it's problematic if anyone ever derives from B: then you're
> constructing the wrong kind of object.


.... that makes sense. Oh well... guess that kills that idea (Triple-
DES wrote the same thing)! Thanks for pointing that out.

> Assignment operator or not has nothing to with the above issue.


In the above case, no, since the default assignment operator works.
But if you say "(*this) = B()", and B is something like, say:

class B {
// ...
int *some_array_that_i_allocated;
};

Then wouldn't you have to make sure you implemented a proper
assignment operator that let "(*this) = B()" free
"some_array_that_i_allocated" first?

Jason
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-27-2008
* http://www.velocityreviews.com/forums/(E-Mail Removed):
> On Feb 27, 2:04 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
>> In particular it's problematic if anyone ever derives from B: then you're
>> constructing the wrong kind of object.

>
> ... that makes sense. Oh well... guess that kills that idea (Triple-
> DES wrote the same thing)! Thanks for pointing that out.
>
>> Assignment operator or not has nothing to with the above issue.

>
> In the above case, no, since the default assignment operator works.
> But if you say "(*this) = B()", and B is something like, say:
>
> class B {
> // ...
> int *some_array_that_i_allocated;
> };
>
> Then wouldn't you have to make sure you implemented a proper
> assignment operator that let "(*this) = B()" free
> "some_array_that_i_allocated" first?


Assuming it was more realistic (the above easily fixed using std::vector): yes.

Check out the swap idiom for implementing assignment operator in terms of copy
constructor.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
 
 
 
Juha Nieminen
Guest
Posts: n/a
 
      02-27-2008
Alf P. Steinbach wrote:
> reset() { *this = B(); }


Btw, a bit related to that, can anyone spot some problem with this:

A::A(const A& rhs)
{
*this = rhs;
}


A& operator=(const A& rhs)
{
// Tons of member variable assignments here
}

The idea would be, of course, to save writing all the member variable
initializations twice.

(I really wish there was a way to call the compiler-generated "default
copy constructor" and the "default assignment operator" from your own
versions, so that you only had to concentrate on those members for which
the default assignment is not enough. The problem with having to
construct/assing every single member manually is that if you add a new
member variable in the future you have to remember to add it to both the
copy constructor and assignment operator. If you forget, the compiler
won't give you even a warning, and the program may malfunction in
strange ways, which will sometimes be very hard to debug.)
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-27-2008
* Juha Nieminen:
> Alf P. Steinbach wrote:
>> reset() { *this = B(); }

>
> Btw, a bit related to that, can anyone spot some problem with this:
>
> A::A(const A& rhs)
> {
> *this = rhs;
> }
>
>
> A& operator=(const A& rhs)
> {
> // Tons of member variable assignments here
> }
>
> The idea would be, of course, to save writing all the member variable
> initializations twice.


It is again a problem with derived classes, but here "only" with efficiency and
expectations of maintainance programmers (the latter the most serious).

Another problem is that it requires data members to be default-constructible.



> (I really wish there was a way to call the compiler-generated "default
> copy constructor" and the "default assignment operator" from your own
> versions, so that you only had to concentrate on those members for which
> the default assignment is not enough. The problem with having to
> construct/assing every single member manually is that if you add a new
> member variable in the future you have to remember to add it to both the
> copy constructor and assignment operator. If you forget, the compiler
> won't give you even a warning, and the program may malfunction in
> strange ways, which will sometimes be very hard to debug.)


That's what the swap idiom for assignment is all about (in addition to exception
safety).


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
Jeff Schwab
Guest
Posts: n/a
 
      02-27-2008
Alf P. Steinbach wrote:
> * (E-Mail Removed):
>> On Feb 26, 11:12 pm, Jeff Schwab <(E-Mail Removed)> wrote:
>>> Alf P. Steinbach wrote:
>>>> * Christopher:
>>>> reset() { *this = B(); }
>>> I've never seen that before, but it does seem like a neat idea.
>>>
>>> It seems like it would be (generally speaking) twice as slow as manual
>>> member-by-member initialization

....
> No. Thinking about that would be premature optimization.


Whether it's slower is a completely separate issue from whether its
speed matters, or whether we "should" be thinking about it yet.
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-27-2008
On Feb 27, 6:22 am, "(E-Mail Removed)"
<(E-Mail Removed)> wrote:
> On Feb 26, 11:12 pm, Jeff Schwab <(E-Mail Removed)> wrote:


> > Alf P. Steinbach wrote:
> > > * Christopher:
> > > reset() { *this = B(); }


> > I've never seen that before, but it does seem like a neat
> > idea.


> > It seems like it would be (generally speaking) twice as slow
> > as manual member-by-member initialization, because of (a)
> > the temporary object's constructor, plus (b) the assignment
> > operator.


If it makes a measurable difference, make the functions inline,
and let the compiler's optimizer take care of it.

> Just now I managed to get this working using placement new. Is
> there anything bad that can happen with the following code
> (assuming the constructor does not throw any exceptions):


> class B {
> public:
> B () { }
> ~B () { }


> void Reset () {
> this->~B();
> new (this) B();
> }
> };


Well assuming the constructor doesn't throw any exceptions, and
assuming that no one ever derives from the class, and probably
assuming one or two other things, it's OK.

In general, there are just too many assumptions in there for me
to ever want to see it in production code.

> I've done a good amount of testing on that just now and it
> seems to work fine (even when B has other complex object
> members, etc.).


Try adding a virtual member (say a virtual destructor), then
deriving. I think you'll find some mighty strange behavior.
And of course, it's very, very hard to guarantee that a
constructor doesn't throw.

--
James Kanze (GABI Software) email:(E-Mail Removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-27-2008
On Feb 27, 11:48 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> * Juha Nieminen:
> > Alf P. Steinbach wrote:
> >> reset() { *this = B(); }


> > Btw, a bit related to that, can anyone spot some problem
> > with this:


> > A::A(const A& rhs)
> > {
> > *this = rhs;
> > }


> > A& operator=(const A& rhs)
> > {
> > // Tons of member variable assignments here
> > }


> > The idea would be, of course, to save writing all the member variable
> > initializations twice.


> It is again a problem with derived classes, but here "only"
> with efficiency and expectations of maintainance programmers
> (the latter the most serious).


I don't think it's really a problem with derived classes,
because at this point, they don't exist yet. (Their constructor
hasn't yet been called.) However...

> Another problem is that it requires data members to be
> default-constructible.


Worse: if some of the data has do nothing constructors, and the
operator= expects it to be initialized. Pointers to dynamic
memory would be a classical case---if the operator= results in
the old values being passed to delete, delete's not going to
like getting passed some random bits.

As you say...

> > (I really wish there was a way to call the
> > compiler-generated "default copy constructor" and the
> > "default assignment operator" from your own versions, so
> > that you only had to concentrate on those members for which
> > the default assignment is not enough. The problem with
> > having to construct/assing every single member manually is
> > that if you add a new member variable in the future you have
> > to remember to add it to both the copy constructor and
> > assignment operator. If you forget, the compiler won't give
> > you even a warning, and the program may malfunction in
> > strange ways, which will sometimes be very hard to debug.)


> That's what the swap idiom for assignment is all about (in
> addition to exception safety).


In simple cases, you may be able to do even simpler than the
swap idiom, but it is the guaranteed general solution (provided
all of the members support it as well).

--
James Kanze (GABI Software) email:(E-Mail Removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
jason.cipriani@gmail.com
Guest
Posts: n/a
 
      02-27-2008
"Alf P. Steinbach" <(E-Mail Removed)> wrote in message news:
(E-Mail Removed)...
> Check out the swap idiom for implementing assignment operator in terms of copy
> constructor.


Is that something like this (from http://www.gotw.ca/gotw/059.htm):

T& T:perator=( const T& other )
{
T temp( other ); // do all the work off to the side
Swap( temp ); // then "commit" the work using
return *this; // nonthrowing operations only
}

And one advantage is there's really not much to do in the = operator,
since you are using the constructors to do a lot the work? So a
Reset() might simply be:

void T::Reset () {
Swap(T());
}

But in that case... I mean, is there a way to implement Swap() that
doesn't involve explicitly typing out code to swap all of the members?
Since you just need to do a "shallow" swap... is something like this
reliable:

void T::Swap (T &other) {
char temp[sizeof(T)];
memcpy(temp, &other, sizeof(T));
memcpy(&other, *this, sizeof(T));
memcpy(*this, temp, sizeof(T));
}

That seems like a horrible way to do it... also, what if "other" is
something derived from T? Is there a way to just use the compilers
default assignment operator even when you've defined your own? Or does
it always make sense to use the "pimpl" idiom when implementing a swap
function, so that you don't have to do something like that?

Jason
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-27-2008
* (E-Mail Removed):
> "Alf P. Steinbach" <(E-Mail Removed)> wrote in message news:
> (E-Mail Removed)...
>> Check out the swap idiom for implementing assignment operator in terms of copy
>> constructor.

>
> Is that something like this (from http://www.gotw.ca/gotw/059.htm):
>
> T& T:perator=( const T& other )
> {
> T temp( other ); // do all the work off to the side
> Swap( temp ); // then "commit" the work using
> return *this; // nonthrowing operations only
> }


Yes, except you can do it neater:

T& operator=( T other )
{
swap( other );
return *this;
}


> And one advantage is there's really not much to do in the = operator,
> since you are using the constructors to do a lot the work?


Main advantages are exception safety and avoiding redundant code (always a
source of bugs and extra work).


> So a
> Reset() might simply be:
>
> void T::Reset () {
> Swap(T());
> }


Well no, it would have to be

void reset() { T().swap( *this ); }


> But in that case... I mean, is there a way to implement Swap() that
> doesn't involve explicitly typing out code to swap all of the members?
> Since you just need to do a "shallow" swap... is something like this
> reliable:
>
> void T::Swap (T &other) {
> char temp[sizeof(T)];
> memcpy(temp, &other, sizeof(T));
> memcpy(&other, *this, sizeof(T));
> memcpy(*this, temp, sizeof(T));
> }


It all depends. Some member might have a pointer back to the object, say.
However, there is a nice little elegant facility for iterating through members
in a little side project of Boost.Spirit, I forget the name (Poenix?), and
possibly that might serve as foundation for a general implementation?


> That seems like a horrible way to do it...


memcpy, yes, horrible.


> also, what if "other" is something derived from T?


The swap idiom takes care of that, whereas other idioms may not necessarily take
care of that.


> Is there a way to just use the compilers
> default assignment operator even when you've defined your own?


Depends which assignment operator you have defined. But if you have defined
the copy assignment operator, then no. On the other hand, the compiler will use
your copy assignment operator when it defines one for a derived class.


> Or does
> it always make sense to use the "pimpl" idiom when implementing a swap
> function, so that you don't have to do something like that?


I don't see what the PIMPL idiom has to do with this, but possibly you mean
having a pointer to the real object state so that you only need to swap
pointers. Well that's what std::vector etc. has. And they also have swap
member functions, so, very efficient.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-27-2008
* James Kanze:
> On Feb 27, 11:48 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
>> * Juha Nieminen:
>>> Alf P. Steinbach wrote:
>>>> reset() { *this = B(); }

>
>>> Btw, a bit related to that, can anyone spot some problem
>>> with this:

>
>>> A::A(const A& rhs)
>>> {
>>> *this = rhs;
>>> }

>
>>> A& operator=(const A& rhs)
>>> {
>>> // Tons of member variable assignments here
>>> }

>
>>> The idea would be, of course, to save writing all the member variable
>>> initializations twice.

>
>> It is again a problem with derived classes, but here "only"
>> with efficiency and expectations of maintainance programmers
>> (the latter the most serious).

>
> I don't think it's really a problem with derived classes,
> because at this point, they don't exist yet. (Their constructor
> hasn't yet been called.) However...


Well, I was thinking of the above as a pattern, used also in derived classes.

Then a call chain

Derived:erived
-> Base::Base
-> Base::Base[init-list] // Base members def-inits
-> Base::Base[body]
-> Base:perator=() // Base members assigned
-> Derived:erived[init-list] // Derived members def-inits.
-> Derived:erived[body]
-> Derived:perator=()
-> Base:perator=() // Base members assigned 2nd time.
-> [assignment statements] // Derived members assigned


[snip]
> In simple cases, you may be able to do even simpler than the
> swap idiom


?

>, but it is the guaranteed general solution (provided
> all of the members support it as well).



Cheers,

- Alf


--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
You will not be able to reset the access - can't reset. jc MCAD 0 12-09-2007 08:58 PM
A constructor calling another constructor (default constructor)? Generic Usenet Account C++ 10 11-28-2007 04:12 AM
CISCO 7XX / 761 password reset or reset to factory defaults Philipp Flesch Cisco 3 06-18-2006 07:47 PM
Confused by the constructor and cop-constructor. Xiangliang Meng C++ 9 06-25-2004 03:44 PM
why it's not possible calling constructor from constructor? Giulio C++ 9 06-25-2003 03:56 PM



Advertisments