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
Thanks for the detailed response!

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

>
> Well no, it would have to be
>
> void reset() { T().swap( *this ); }


How is that different from "this->Swap(T())"?

> 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?


Phoenix is here:

http://www.boost.org/libs/spirit/phoenix/index.html

From what I can tell skimming through the introduction now, it looks
like a project to somehow facilitate functional programming in C++. I
bet there's something good in there somewhere. I'll post back here if
I find a way to do it -- "Phoenix" looks like the kind of thing that
could distract me for hours so I'm going to have to put it off until
the weekend .

>> 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.


So... if I have:

void T::Swap (T &other) {
}

And 'other' is something derived from T... my Swap() would only swap
the members of 'other' that were in the T base, leaving any new
members in the derived class untouched. Is that behavior part of the
"swap idiom" (only swapping the members that two objects have in
common, leaving the others untouched) (it does seem reasonable)?

>> 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.


That is what I meant. Sorry for being unclear -- I was secretly still
looking at http://www.gotw.ca/gotw/059.htm and going off the rest of
the example there; where they did just that -- swapped pointers to the
object's state (and it's straight forward to update any state pointer
back to the object too; just as long as, like you mentioned above,
none of the members in the object state point back to the object
itself, which shouldn't happen unless you were sloppy).

Thanks for your time,
Jason
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-27-2008
* http://www.velocityreviews.com/forums/(E-Mail Removed):
> Thanks for the detailed response!
>
>>> So a
>>> Reset() might simply be:
>>>
>>> void T::Reset () {
>>> Swap(T());
>>> }

>> Well no, it would have to be
>>
>> void reset() { T().swap( *this ); }

>
> How is that different from "this->Swap(T())"?


If you just try it you'll see...


>> 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?

>
> Phoenix is here:
>
> http://www.boost.org/libs/spirit/phoenix/index.html
>
> From what I can tell skimming through the introduction now, it looks
> like a project to somehow facilitate functional programming in C++. I
> bet there's something good in there somewhere. I'll post back here if
> I find a way to do it -- "Phoenix" looks like the kind of thing that
> could distract me for hours so I'm going to have to put it off until
> the weekend .


I checked.

I think what I was thinking of was Fusion 2.0,

<url:
http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/fusion/extension/ext_full.html>.

Not sure how that can be simplified down, though.


>>> 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.

>
> So... if I have:
>
> void T::Swap (T &other) {
> }
>
> And 'other' is something derived from T... my Swap() would only swap
> the members of 'other' that were in the T base, leaving any new
> members in the derived class untouched. Is that behavior part of the
> "swap idiom" (only swapping the members that two objects have in
> common, leaving the others untouched) (it does seem reasonable)?


Yeah, it's just usual C++ slicing.

However, for assignment nothing happens to the source object.

It's a (sliced or converted to) copy that's swapped.


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
 
 
 
 
James Kanze
Guest
Posts: n/a
 
      02-28-2008
On Feb 27, 5:47 pm, "(E-Mail Removed)"
<(E-Mail Removed)> wrote:
> "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 (fromhttp://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
> }


That's the way I write it. Two other frequent alternatives are:

T&
T:perator=(
T const& other )
{
T( other ).swap( *this ) ;
return *this ;
}

and

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

(Note that in the last case, the copy is actually done by the
caller, so you don't need it in the operator= function.)

> 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?


The real advantage is that it is the simplest way to ensure that
all operations which can fail take place before you modify
anything in the object being assigned to.

> 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?


Not really, since some of the members may have specialized swap
algorithms.

> 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));
> }


No.

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


It is, since it involves undefined behavior.

Note that it's entirely possible that the swap idiom cannot be
used for certain classes---if there's no way to do a guaranteed
nothrow swap of some base class or member.

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


What about it? Assignment cannot change the type of the object
being assigned, so you only assign the T part of the derived
object. (In general, assignment and polymorphism don't work
well together, and if a class is designed to be used as a base
class, it's usually best to inhibit assignment in it.)

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


No. Presumably, if you provide your own assignment operator,
it's because the compiler's default assignment operator doesn't
do what you want.

> 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?


In a certain sense, it always makes sense to use the swap idiom
for assignment if you use the compilation firewall idiom.
Except that in the case of the compilation firewall, you're
dealing with an easily recognizable idiom, and even the standard
implementation of operator= is so simple that I don't find it
worth the bother. Generally speaking:

-- if the class corresponds to an established idiom (like the
compilation firewall), then use the assignment operator for
the idiom, otherwise,

-- if all members support value nothrow value assignment, then
the compiler defined assignment operator is all you need; no
point in the swap idiom, otherwise,

-- if all of the members support value assignment, and you
don't need the strong exception guarantee, then the compiler
defined assignment operator is all you need as well,
otherwise,

-- if all of the members are either non-class types, or class
types which support a nothrow swap (generally, which have a
member function swap), then use the swap idiom, using
std::swap for the non-class types, and the nothrow swap for
the class types, otherwise

-- you're on your own, and it may be difficult.

In practice, if all of the libraries you're using understand the
swap idiom, and have been written in consequence, the fourth
case will be rather frequent. If you have to deal with legacy
libraries, however, you may find that you fall into the fifth
case more often than you'd like. In such cases, I find that it
often is convenient to use the compilation firewall idiom in
such cases, to move them up to the first case above.

--
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-28-2008
On Feb 27, 7:39 pm, "(E-Mail Removed)"
<(E-Mail Removed)> wrote:
> Thanks for the detailed response!


> >> So a Reset() might simply be:


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


> > Well no, it would have to be


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


> How is that different from "this->Swap(T())"?


It's legal code. It's hard to imagine swap taking anything but
a non-const reference as an argument, and of course, you can't
initialize a non-const reference with a temporary (even if some
compilers fail to detect the error).

[...]
> >> 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.


(Not knowing what the problem is, I can't say whether swap takes
care of it or not. I don't see how swap has any impact one way
or the other, but perhaps Alf sees a problem that I don't.)

> So... if I have:


> void T::Swap (T &other) {
> }


> And 'other' is something derived from T... my Swap() would
> only swap the members of 'other' that were in the T base,
> leaving any new members in the derived class untouched.


How could it do otherwise, given that it doesn't know anything
about the derived class?

But of course, that question is moot in the swap idiom for
assignment, since other will never be an object with a derived
class type.

> Is that behavior part of the "swap idiom" (only swapping the
> members that two objects have in common, leaving the others
> untouched) (it does seem reasonable)?


It doesn't seem reasonable to me to support assignment if the
class is to be used as a base 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.


> That is what I meant. Sorry for being unclear -- I was
> secretly still looking athttp://www.gotw.ca/gotw/059.htmand
> going off the rest of the example there; where they did just
> that -- swapped pointers to the object's state (and it's
> straight forward to update any state pointer back to the
> object too; just as long as, like you mentioned above, none of
> the members in the object state point back to the object
> itself, which shouldn't happen unless you were sloppy).


In the case of the compilation firewall idiom, you can get even
simpler than swap. The standard operator= in that case is:

T&
T:perator=( T const* other )
{
Impl* tmp = new Impl( *other.myImpl ) ;
// or = other.myImpl->clone()...
delete myImpl ;
my Impl = tmp ;
return *this ;
}

The swap idiom is also quite acceptable in this case, but
perhaps for historical reasons, I find the above more idiomatic.

--
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-28-2008
On Feb 27, 8:22 pm, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> * (E-Mail Removed):


> > Thanks for the detailed response!


> >>> So a
> >>> Reset() might simply be:


> >>> void T::Reset () {
> >>> Swap(T());
> >>> }
> >> Well no, it would have to be


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


> > How is that different from "this->Swap(T())"?


> If you just try it you'll see...


Maybe, maybe not. I think that there are still some widespread
compilers which let it through, without so much as a warning
(although it has been illegal now for almost 20 years).

--
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-28-2008
On Feb 27, 6:13 pm, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> * 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


Good point. Arguable, if your class supports assignment, then
assigning the same value twice shouldn't cause any problems.
But it's certainly something I'd prefer avoiding, just in case.
(Of course, arguably, if your class is being used as a base
class, it shouldn't support assignment at all. But that's
another issue.)

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


> ?


You wouldn't really use the swap idiom for something like
complex, would you? (For that matter, would you ever use it if
the compiler generated assignment operator would work?)

Even in the case of the compilation firewall idiom, I tend to
just use the classical assignment operator---probably because I
was using the compilation firewall idiom long before I'd ever
heard of the swap idiom. (It was called the Cheshire cat idiom
back then---the implementation just disappears, until all you
see is its smile, and then not even that.) It's in my fingers.
But if I were to change, I'd use a boost::scoped_ptr, and
simply:

T&
T:perator=( T const& other )
{
myImpl.reset( new Impl( *other.myImpl ) ) ;
return *this ;
}

You could use the swap idiom, but it seems more complex here,
and it is certainly not necessary.

Note that in the case of the compilation firewall idiom, part of
the reason why the swap idiom isn't necessary is because the
compilation firewall idiom is an established pattern. I don't
have to think about things like exception safety issues, because
the "standard" solutions are known to be exception safe. In a
more general case, I might adopt the swap idiom simply because
it means that I know I have exception safety, without having to
think any further about it.

Not that I don't like thinking, but there are always enough
other things to think about, for which there are no simple
solutions, to keep me occupied. Anytime there's a simple
solution which is guaranteed to work, I'm all for it.

--
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
 
 
 
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