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 | » ]
Chuck Allison

Posts: 63
Nickname: cda
Registered: Feb, 2003

The Law of The Big Two Posted: Oct 1, 2004 9:00 AM
Reply to this message Reply
Advertisement
In this inaugural installment of their new column, Matthew Wilson and Bjorn Karlsson update the well-known Law of The Big Three, explaining which one of those member functions is not always needed.

www.artima.com/cppsource/bigtwo.html


Colin Rafferty

Posts: 1
Nickname: craffert
Registered: Oct, 2004

Final version of constructor not exception-safe. Posted: Oct 1, 2004 2:23 PM
Reply to this message Reply
The problem is that we cannot depend on the order of initialization. Here is the final version of the constructor from the article:

Example() :
p_(new SomeResource()),
p2_(new SomeResource()) {}

The second call to new can occur before p_ is initialized with the result of the first call to new. If the second call throws an exception, the result of the first call will be leaked.

The only way to be certain that this code is exception-safe is the unfortunate method of initializing the members inside the constructor.

Example()
{
p_.reset(new SomeResource());
p2_.reset(new SomeResource();
}

indranil banerjee

Posts: 11
Nickname: indranilb
Registered: Feb, 2004

Re: Final version of constructor not exception-safe. Posted: Oct 2, 2004 1:54 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.

This only works in constructor member initialisation lists. You're right that for function calls this would not be exception safe.

foo(RAII(new X()), RAII(new X());

The above code would not be safe because, as you pointed out, the two X objects could be created before either is assigned to the RAII wrappers. So if the first new X() succeeded and the second new X() threw, then the RAII would not clean up the first X.

Vesa Karvonen

Posts: 116
Nickname: vkarvone
Registered: Jun, 2004

Re: The Rule of The Big Two Posted: Oct 2, 2004 10:49 AM
Reply to this message Reply
I just wanted to note that the delete -operator does nothing if the pointer given to it is 0. This means that the checks in the following code snippet taken verbatim from the article are unnecessary.
    if (p_)
      delete p_;
    if (p2_)
      delete p2_;

I mention this, because many unnecessary "safety idioms", like this, get picked up by newbie programmers. Unnecessary checks only increase code complexity.

Vesa Karvonen

Posts: 116
Nickname: vkarvone
Registered: Jun, 2004

Re: The Rule of The Big Two Posted: Oct 3, 2004 2:39 PM
Reply to this message Reply
After a diligent rereading of the article, I had to grab the keyboard again. The below code taken from the article is extremely error-prone:

template <typename T> class RAII {
// ...
operator T*() {
return p_;
}

operator const T*() const {
return p_;
}
// ...
};


It is just too easy to accidentally return (or pass as an argument) a dangling pointer from (to) a function when the conversion from a smart pointer to a plain pointer is implicit. I'm very disappointed that the authors, who really should know better, do not mention this in the article. Having debugged bugs caused by conversion operators like above, I can only advice everyone to never provide such dangerous implicit conversions.

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: The Rule of The Big Two Posted: Oct 3, 2004 4:14 PM
Reply to this message Reply
Agreed, with contrition <g>. We'll ask Chuck to amend the article so.

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: The Rule of The Big Two Posted: Oct 3, 2004 4:18 PM
Reply to this message Reply
Gah! I'd expected that the reply I just made would have been sub-threaded, or some such, to indicate which post it was a response to.

It was responding to the comment regarding the redundant ifs around the deletions

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: The Rule of The Big Two Posted: Oct 3, 2004 4:27 PM
Reply to this message Reply
[This is a reply to the "extremely error-prone" issue.]

I think you've, perhaps reasonably, mistaken the intent of the RAII class. As stated in the text, it is "intended mainly for the purpose of adding RAII to simple classes".

I concede, in hindsight, that that should be better expressed, i.e., something along the lines of "implicit conversions are dangerous (which I assure you we do know <g>), and should not be used in smart pointer classes in general. There are much better alternatives for generalised treatment of raw and smart pointers, which we intend to deal with in a future instalment. RAII does have them, however, because it is solely intended for use inside other classes, and to be a plug in replacement for raw pointers in that context."

Alternatively, we could just have dropped the implicit conversions, which would barely have troubled our use of the class, and saved you consternation and our readers any confusion.

In hindsight, the latter approach is probably the better one. I'll get together with Bjorn, and we'll look into adjusting the article accordingly.

Thanks for the keen eye, and the willing fingers. :-)

Daniel Teske

Posts: 5
Nickname: teske
Registered: Oct, 2004

Re: The Rule of The Big Two Posted: Oct 3, 2004 11:02 PM
Reply to this message Reply
>Example(const Example& other)
> : p_(new SomeResource(other.p_ ? *other.p_ : 0)),
> p2_(new SomeResource(other.p2_ ? *other.p2_ : 0)) {}

Isn't that supposed to be:
Example(const Example& other)
: p_(other.p_.get()? new SomeResource( *other.p_) : 0),
p2_(other.p2_.get()? new SomeResource( *other.p2_): 0)

Daniel Teske

Posts: 5
Nickname: teske
Registered: Oct, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 12:09 AM
Reply to this message Reply
I understand why the authors didn't want to use auto_ptr, but the standard has another less known smart pointer:
const auto_ptr<>

const auto_ptr<> doesn't allow copy constructing and operator=. (Because both take a non-const reference).
It can't release() its ownership and it can't be reset().

The only valid constructor for const auto_ptr takes a plain pointer. So a const auto_ptr accuires ownership at the point of construction and owns the object until its destruction.

const auto_ptr<> fits RAII better than auto_ptr<>.
Because const auto_ptr<> has a very clear meaning, it should be used where possible. (The example isn't fleshed out enough to say for sure if it could be used.)

Vesa Karvonen

Posts: 116
Nickname: vkarvone
Registered: Jun, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 12:12 AM
Reply to this message Reply
> I think you've, perhaps reasonably, mistaken the intent of
> the RAII class. As stated in the text, it is "intended
> mainly for the purpose of adding RAII to simple
> classes
".

I did read that line and I understood the intention of the class on the first reading. (That's why I emphasized being diligent.) However, being an exercise tutor on a C++ course and recommending innocent students to read this (otherwise excellent!) article, I simply can't let things like this slip through without critique.

> Alternatively, we could just have dropped the implicit
> conversions, which would barely have troubled our use of
> the class, and saved you consternation and our readers any
> confusion.

Yes. I think that would have been the better choice. It just doesn't take a lot of effort (or a lot of lines) to provide a few more operators. Those few lines would very likely save someone from a debugging/fixing nightmare.

Bjorn Karlsson

Posts: 9
Nickname: bfk
Registered: Aug, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 12:18 AM
Reply to this message Reply
Hello Vesa,

> I did read that line and I understood the intention of the
> class on the first reading. (That's why I emphasized being
> diligent.) However, being an exercise tutor on a
> C++ course and recommending innocent students to read this
> (otherwise excellent!) article, I simply can't let things
> like this slip through without critique.

You are absolutely right (and diligent!).

> Yes. I think that would have been the better choice. It
> just doesn't take a lot of effort (or a lot of lines) to
> provide a few more operators. Those few lines would very
> likely save someone from a debugging/fixing nightmare.

I'll update the article during the day. Thanks!

Bjorn

Bjorn Karlsson

Posts: 9
Nickname: bfk
Registered: Aug, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 1:03 AM
Reply to this message Reply
Hello Daniel,

> Isn't that supposed to be:
> Example(const Example& other)
> : p_(other.p_.get()? new SomeResource( *other.p_) :
> p_) : 0),
> p2_(other.p2_.get()? new SomeResource( *other.p2_):
> r.p2_): 0)

No, the original RAII class had an implicit conversion to T*, so there was no (need for) get(). However, the constructor of the Example class always allocates (and only deallocates in the destructor) p_ and p2_, so the code should be:

Example(const Example& other) :
p_(new SomeResource(*other.p_)),
p2_(new SomeResource(*other.p2_)) {}

Cheers,
Bjorn

Bjorn Karlsson

Posts: 9
Nickname: bfk
Registered: Aug, 2004

Re: The Rule of The Big Two Posted: Oct 4, 2004 1:14 AM
Reply to this message Reply
> I understand why the authors didn't want to use auto_ptr,
> but the standard has another less known smart pointer:
> const auto_ptr<>

Good point! But having a member of type const auto_ptr makes implementing the copy assignment operator problematic.

> const auto_ptr<> doesn't allow copy constructing and
> operator=. (Because both take a non-const reference).
> It can't release() its ownership and it can't be reset().

Exactly. This is a nice idiom for managing scoped resources (although boost::scoped_ptr is a better choice, in my opinion).

> const auto_ptr<> fits RAII better than auto_ptr<>.
> Because const auto_ptr<> has a very clear meaning, it
> should be used where possible. (The example isn't fleshed
> out enough to say for sure if it could be used.)

It couldn't, due to the copy assignment operator. But a future article talks about this very topic, including the use of const auto_ptr.

Cheers,
Bjorn

Uwe Schnitker

Posts: 9
Nickname: uwe
Registered: Aug, 2003

Re: The Rule of The Big Two Posted: Oct 4, 2004 1:28 AM
Reply to this message Reply
Why stop here?

Why not make it "The Rule of The Big Zero"?

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

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

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

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

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