The Artima Developer Community
Sponsored Link

Articles Forum
The Law of The Big Two

36 replies on 3 pages. Most recent reply: May 9, 2008 10:32 AM by m limber

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 36 replies on 3 pages [ « | 1 2 3 | » ]
Daniel Teske

Posts: 5
Nickname: teske
Registered: Oct, 2004

Re: Example(const Example& other) Posted: Oct 4, 2004 2:08 AM
Reply to this message Reply
Advertisement
Sorry, the .get() is there because I tested the example with a const auto_ptr<>.
But I still think there is an error in the code.
Using excessive formating and ignoring p2_:
Article:

p_(new SomeResource(
other.p_ ?
*other.p_ :
0))


My:

p_(
other.p_?
new SomeRessource(*other.p_):
0)


Because if other.p_ == 0 then the version in the article will call new SomeRessource(0), which normally doesn't compile.

You are right that other.p_ can't be 0.

Daniel Teske

Posts: 5
Nickname: teske
Registered: Oct, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 3:03 AM
Reply to this message Reply
>It couldn't, due to the copy assignment operator.
Hmm, I don't quite understand where the problem is.

class Example calls SomeResource.operator=() to copy p_ and p2_. Right?
And i see no reason why a Example class using const auto_ptr<> couldn't use SomeRessource.operator=().
So what's wrong with this:
class Example {   
    const auto_ptr<SomeResource> p_;
const auto_ptr<SomeResource> p2_;

...
  Example& operator=(const Example& other) {
// Self assignment?
if (this==&other)
return *this;
      *p_  = *other.p_;
*p2_ = *other.p2_;
      return *this;
}



>boost::scoped_ptr
Well scoped_ptr and const auto_ptr<> are more or less the same.

Steve Love

Posts: 20
Nickname: essennell
Registered: Sep, 2004

Re: Final version of constructor not exception-safe. Posted: Oct 4, 2004 3:28 AM
Reply to this message Reply
> It is safe. The commas in the member initialisation list
> are sequence points. Therefore in the example the first
> SomeResource will be allocated, constructed and assigned
> to p_ before the second SomeResource begins construction.
>

No, the commas are *not* sequence points. The order of initialization is defined in the data-member list for the class. E.g.

struct T
{
T() : p( new X ), q( new X ) { }

X * q;
X * p;
};

q is *always* initialized before p.

In the article, the order of the members matched the order in the initializer list, so all is fine - and no confusion to anyone.

Bjorn Karlsson

Posts: 9
Nickname: bfk
Registered: Aug, 2004

Re: Example(const Example& other) Posted: Oct 4, 2004 4:03 AM
Reply to this message Reply
[snip]
> But I still think there is an error in the code.

Yes, you're right (the code didn't match the one I have locally). The error goes away when the test is omitted.

Thanks,
Bjorn

Bjorn Karlsson

Posts: 9
Nickname: bfk
Registered: Aug, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 4:30 AM
Reply to this message Reply
> >It couldn't, due to the copy assignment operator.
> Hmm, I don't quite understand where the problem is.

The problem in the general case is that the auto_ptr must always own a dynamically allocated object that supports (copy) assignment.

In this case, the above is always true, so const auto_ptr would indeed have worked.

> Well scoped_ptr and const auto_ptr<> are more or less the
> same.

There are a couple of differences that I think are important:

* A scoped_ptr can be reset, a const auto_ptr cannot.
* A const auto_ptr requires the reader of the code to recognize the importance of the constness, whereas scoped_ptr is always true to its name, and simply does not transfer ownership, period.

Cheers,
Bjorn

Bjorn Karlsson

Posts: 9
Nickname: bfk
Registered: Aug, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 4:37 AM
Reply to this message Reply
Hello Uwe,

> Why stop here?
>
> Why not make it "The Rule of The Big Zero"?

Hey, have you been reading through my drafts? ;-)

> When the object is copied, you will either want to have
> reference counting or deep copy semantic for its owned
> resources.

Yes.

> If you opt for reference semantics, a suitable smart
> pointer can just do it.

Exactly.

> To enable deep copy semantic, the smart pointer would need
> to accept some copy mechanism function (object) pointer.
> You would either use a creation function rsp. a factory
> function if a specific type has to be created, or make use
> of the clone idiom if a polymorphic copy is needed. (If
> you are not afraid of Mad COW Disease, you could even try
> to figure out a generic COW solution.)

There are variations and techniques relating to create/clone/copy that make it possible to cover many use cases with little or no extra effort on behalf of the user.

> Some very special cases might remain unsolved, but often
> the "Rule of The Big Zero" would be a good - and probably
> very attractive - solution.

I agree. The general case is the "Law of the Big Three". The typical case is the "Law of the Big Two". The ideal case is the "Law? What Law?", but it typically comes at a cost in space and/or time (that may or may not be acceptable). More on this interesting topic in a future installment...

Cheers,
Bjorn

Uwe Schnitker

Posts: 9
Nickname: uwe
Registered: Aug, 2003

Re: The Rule of The Big Two Posted: Oct 4, 2004 6:41 AM
Reply to this message Reply
> Hello Uwe,
>
> > Why stop here?
> >
> > Why not make it "The Rule of The Big Zero"?
>
> Hey, have you been reading through my drafts? ;-)

No, I've been reading your mind. ;-)

Sometimes, a C++ programmer MUST dive down through all these levels of abstraction, to get the optimum efficiency.

> > When the object is copied, you will either want to have
> > reference counting or deep copy semantic for its owned
> > resources.
>
> Yes.
>
> > If you opt for reference semantics, a suitable smart
> > pointer can just do it.
>
> Exactly.
>
> > To enable deep copy semantic, the smart pointer would
> need
> > to accept some copy mechanism function (object)
> pointer.
> > You would either use a creation function rsp. a factory
> > function if a specific type has to be created, or make
> use
> > of the clone idiom if a polymorphic copy is needed. (If
> > you are not afraid of Mad COW Disease, you could even
> try
> > to figure out a generic COW solution.)
>
> There are variations and techniques relating to
> create/clone/copy that make it possible to cover many use
> cases with little or no extra effort on behalf of the
> user.

Some of these techniques will look quite "alexandresque", won't they?

> > Some very special cases might remain unsolved, but
> often
> > the "Rule of The Big Zero" would be a good - and
> probably
> > very attractive - solution.
>
> I agree. The general case is the "Law of the Big Three".
> The typical case is the "Law of the Big Two". The ideal
> case is the "Law? What Law?",

This is sometimes called "true encapsulation".

> but it typically comes at a
> cost in space and/or time (that may or may not be
> acceptable). More on this interesting topic in a future
> installment...

I'm looking forward ...

> Cheers,
> Bjorn

Cheers,

Uwe

Mehul Mahimtura

Posts: 1
Nickname: mehul
Registered: Oct, 2004

Re: The Law of The Big Two Posted: Oct 4, 2004 11:48 AM
Reply to this message Reply
I think smart_ptr's are worth every penny and your law of big 2 will work most of the time. However if someone uses the law of the two in a base class from which a heirarchy of classes are derived then the 'virtual' aspect of the destructor will be lost. Also the compiler will generate non-virtual destructors for all derived classes. This will be a very bad thing.

Of course to correct this they need to declare the base classes' destructor virtual, in which case the compiler will generate virtual destructors

Steve Love

Posts: 20
Nickname: essennell
Registered: Sep, 2004

Re: The Law of The Big Two Posted: Oct 4, 2004 11:22 PM
Reply to this message Reply
> Of course to correct this they need to declare the base
> classes' destructor virtual, in which case the compiler
> will generate virtual destructors

Well one school of thought recommends not deriving from classes with no virtual DTR ;-) (or alternatively always give classes you intend to be derived from a virtual DTR).

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: The Law of The Big Two Posted: Oct 4, 2004 11:44 PM
Reply to this message Reply
> > Of course to correct this they need to declare the base
> > classes' destructor virtual, in which case the compiler
> > will generate virtual destructors

> Well one school of thought recommends not deriving from
> classes with no virtual DTR ;-) (or alternatively always
> give classes you intend to be derived from a virtual DTR).

That's generally excellent advice in runtime polymorphic (aka "classic") C++ world. However, it's a little too coarse-grained, IMO. If you're dealing with types with value semantics, then they're not likely to be in a (well-written) runtime-polymorphic hierarchy. As such, you're unlikely to be deleting them, if at all, via base class pointers.

I think this is especially the case in template-world. It's very useful to be able to apply a compile-time adaptation to (value) types, via templates that inherit from their parameterising types. Again, such types are not going to be deleted by pointers to base class, so there's no need to encumber original class or composite with a vtable.

Naturally, it's open to abuse, but that's why I'm stressing value types. It's most certainly the case that this could be abused if applied to (runtime polymorphic) entity types.

Steve Love

Posts: 20
Nickname: essennell
Registered: Sep, 2004

Re: The Law of The Big Two Posted: Oct 5, 2004 2:59 AM
Reply to this message Reply
> That's generally excellent advice in runtime polymorphic
> (aka "classic") C++ world. However, it's a little too
> coarse-grained, IMO. If you're dealing with types with
> value semantics, then they're not likely to be in a
> (well-written) runtime-polymorphic hierarchy. As such,
> you're unlikely to be deleting them, if at all, via base
> class pointers.

Well that was my point ;-) Value types will not (generally) have virtual DTRs, so you shouldn't (generally) derive from them.

(And I don't know about "classic"; C++ has long been multi-paradigm)

But it's a good point, well made. The difference between Value and Polymorphic/Entity/... behaviour is often not stressed enough.

>
> I think this is especially the case in template-world.
> It's very useful to be able to apply a compile-time
> adaptation to (value) types, via templates that inherit
> from their parameterising types.

Yes. The "Curiously Recurring Template Pattern". The base classes are generally mixin types in this case, tho, and have no state, just behaviour.

> Again, such types are not
> going to be deleted by pointers to base class, so there's
> no need to encumber original class or composite with a
> vtable.

Agreed. My own practice is to make the DTR protected and non-virtual in the base, thus preventing any misuse.

>
> Naturally, it's open to abuse, but that's why I'm
> stressing value types. It's most certainly the case that
> this could be abused if applied to (runtime polymorphic)
> entity types.

When I say "Classes you intend to derive from" I mean "Polymorphic and/or Entity Types"; I should have made that clearer. By Entity Types I mean types generally used to indicate behavioural contract, like Interfaces.

Steve

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: The Law of The Big Two Posted: Oct 5, 2004 3:51 AM
Reply to this message Reply
> > That's generally excellent advice in runtime polymorphic
> > (aka "classic") C++ world. However, it's a little too
> > coarse-grained, IMO. If you're dealing with types with
> > value semantics, then they're not likely to be in a
> > (well-written) runtime-polymorphic hierarchy. As such,
> > you're unlikely to be deleting them, if at all, via base
> > class pointers.
>
> Well that was my point ;-) Value types will not (generally)
> have virtual DTRs, so you shouldn't (generally) derive from them.

Well, I think the point I was making was that because value types
should not be dealt with polymorphically anyway, there's no reason
to *not* derive from them, except for the fact that it might incline
the unwary to start treating them polymorphically.

Personally, I'm quite happy with doing so via templates, but I still
get an uneasy feeling seeing it done in a non-template way. (Too much
exposure to clients' codebases containing classes deriving from
std::string in order to provide an implicit conversion
operator a la CString. Groan ...)

> (And I don't know about "classic"; C++ has long been multi-paradigm)

Fair point. ;)

Maybe it's old farts like me that have spent so many years playing
around before templates were widely usable.

> But it's a good point, well made. The difference between Value and
> Polymorphic/Entity/... behaviour is often not stressed enough.

Agreed. (<blatant>My new book does make this distinction, and looks
a little deeply into the meaning of value types</blatant>)

> > I think this is especially the case in template-world.
> > It's very useful to be able to apply a compile-time
> > adaptation to (value) types, via templates that inherit
> > from their parameterising types.
>
> Yes. The "Curiously Recurring Template Pattern". The base classes are
> generally mixin types in this case, tho, and have no state, just behaviour.

While CRTP is most certainly an example of what I'm talking about, it's
more specialised. The idea I'm talking about is not necessarily recursive.
It looks like the following:

template <class T>
class X
  : public T
{
  . . . // blah blah
};
 
class SomeValueType
{
};
 
typedef X<SomeValueType>  better_value_t;

In my book <sorry, I don't mean to be such a tool ;)> I discuss the concepts
of Veneers and Bolt-ins, which both follow this pattern. Veneers are the
things we're talking about here, for adapting/adorning value-types, since
they are not allowed to add non-static member variables, and must respect
the polymorphic nature of their primary parameterising type.

An example from the STLSoft libs is the mfcstl::cstring_veneer, which adapts
the old CString class into something with a std::basic_string<>-like interface,
i.e. c_str(), iterators, etc.

> > Again, such types are not
> > going to be deleted by pointers to base class, so there's
> > no need to encumber original class or composite with a
> > vtable.
>
> Agreed. My own practice is to make the DTR protected and non-virtual in the
> base, thus preventing any misuse.

Agreed. Herb's new book - Exceptional C++ Style - talks about this specifically.

> > Naturally, it's open to abuse, but that's why I'm
> > stressing value types. It's most certainly the case that
> > this could be abused if applied to (runtime polymorphic)
> > entity types.
>
> When I say "Classes you intend to derive from" I mean "Polymorphic and/or
> Entity Types"; I should have made that clearer. By Entity Types I mean
> types generally used to indicate behavioural contract, like Interfaces.

Agreed.

Steve Love

Posts: 20
Nickname: essennell
Registered: Sep, 2004

Re: The Law of The Big Two Posted: Oct 5, 2004 4:55 AM
Reply to this message Reply
I think we're in violent agreement (or close to it) ;-)

Is this getting off-topic?

> way. (Too much
> exposure to clients' codebases containing classes deriving
> from
> std::string in order to provide an implicit
> conversion
> operator a la CString. Groan ...)

Yup. Or std::vector<> to get a public Length "Property". I've seen 'em!

> Maybe it's old farts like me that have spent so many years
> playing
> around before templates were widely usable.

>8^>

> In my book <sorry, I don't mean to be such a tool ;)>

OK OK, it's been on my to-get list for a while, I admit.

> discuss the concepts
> of Veneers and Bolt-ins, which both follow
> this pattern.

These (and CRTP) are arguably expert-level, best kept well under the covers. STLSoft is a library, and these techniques have a place there, in much the same way as allocators in the Standard Lib (albeit with more utility ;-).

> [me] ...so you shouldn't (generally) derive from them.

hence the "generally".

> > Agreed. My own practice is to make the DTR protected and
> non-virtual in the
> > base, thus preventing any misuse.
>
> Agreed. Herb's new book - Exceptional C++ Style -
> talks about this specifically.

Well I don't have that book, but I definitely picked up the technique from Herb amongst others. Kevlin Henney seems a likely candidate.

Vincent O'Sullivan

Posts: 724
Nickname: vincent
Registered: Nov, 2002

Re: The Rule of The Big Two Posted: Oct 5, 2004 5:02 AM
Reply to this message Reply
It was threaded correctly.

You need to turn on the 'Threaded view' option at the top of the page.

V.

Todd Blanchard

Posts: 316
Nickname: tblanchard
Registered: May, 2003

I'm blown away Posted: Oct 7, 2004 3:42 PM
Reply to this message Reply
that this is still the state of discourse in C++.

Frozen in time for something like 10 years. Still no standard reference counting pointer implementation? Auto_ptr is weird and error prone and this level of fiddling is just madness.

Providing optional GC would be a big win. Providing useful (and standard) counted and counting_ptr pairs would fix a ton of systems, but we are still fooling around with this?

Color me disgusted.

Flat View: This topic has 36 replies on 3 pages [ « | 1  2  3 | » ]
Topic: Rich-Client Misconceptions Previous Topic   Next Topic Topic: Application Factories

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use