Jump to content

  • Log In with Google      Sign In   
  • Create Account


Rule of Three, and const-correctness questions


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
39 replies to this topic

#21 johnmarinelli   Members   -  Reputation: 304

Like
0Likes
Like

Posted 07 February 2013 - 01:08 AM

Wow, thanks for all the replies everyone! 
I'm in the process of following the Rule of Three in my code; after some trial and error, I have a handle on the concept.  It's a small project, so I won't be using smart pointers yet - however I'll keep them in mind for future projects.

 

Once again, thanks for all the insight and time!



Sponsor:

#22 jwezorek   Crossbones+   -  Reputation: 1772

Like
1Likes
Like

Posted 07 February 2013 - 11:45 AM

By the way, to the OP, I think your on the right track wanting to learn about const correctness and the rule of three,.

 

Another good thing to learn at this point, if you don't already know, is how to use the std library smart pointer classes std::unique_ptr and std::shared_ptr.



#23 L. Spiro   Crossbones+   -  Reputation: 12966

Like
2Likes
Like

Posted 07 February 2013 - 06:08 PM

I think you would be on a better track learning the Rule of Two and RAII (repeated because it was likely lost in the slew of other previous replies).

Honestly, RAII probably takes priority over either Rule or Three or Rule of Two, which is not to say the rules are not important, but RAII is just such a core principal that can and should be applied vigorously throughout all of your code, as it prevents memory leaks (even complicated ones caused by exceptions), failure to release file handles, failure to leave critical sections, failure to delete pointers, etc.

 

Failure to follow one of the Rules of X will bite you in the ass when you copy complex objects, but from a performance standpoint that is something you should be trying to avoid as much as possible anyway.  While it is still sometimes necessary to copy complex objects, you will get bitten in the ass far more often by failing to release some resource or leave a critical section, as these cases simply happen magnitudes more often in practice than copying complex objects.

 

So once you have thoroughly employed RAII, Rule of Three no longer makes sense.

Better to prioritize RAII and then go with Rule of Two without wasting time on the less-safe (due to exceptions) and more time-consuming Rule of Three.

 

 

L. Spiro


It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#24 King Mir   Members   -  Reputation: 1934

Like
0Likes
Like

Posted 07 February 2013 - 08:38 PM

Wow, thanks for all the replies everyone! 
I'm
in the process of following the Rule of Three in my code; after some
trial and error, I have a handle on the concept.  It's a small project,
so I won't be using smart pointers yet - however I'll keep them in mind
for future projects.

 

Once again, thanks for all the insight and time!

 

As jwezorek says, you do want to learn and use smart pointers. Even in small projects. Smart pointers and STL containers are the preferred way of managing memory so that you don't have a leak.

 

The rule of three has it's place, but smart pointers make it much more rare.

 

I think you would be on a better track learning the Rule of Two and RAII (repeated because it was likely lost in the slew of other previous replies).

Honestly, RAII probably takes priority over either Rule or Three or Rule of Two, which is not to say the rules are not important, but RAII is just such a core principal that can and should be applied vigorously throughout all of your code, as it prevents memory leaks (even complicated ones caused by exceptions), failure to release file handles, failure to leave critical sections, failure to delete pointers, etc.

 

Failure to follow one of the Rules of X will bite you in the ass when you copy complex objects, but from a performance standpoint that is something you should be trying to avoid as much as possible anyway.  While it is still sometimes necessary to copy complex objects, you will get bitten in the ass far more often by failing to release some resource or leave a critical section, as these cases simply happen magnitudes more often in practice than copying complex objects.

 

So once you have thoroughly employed RAII, Rule of Three no longer makes sense.

Better to prioritize RAII and then go with Rule of Two without wasting time on the less-safe (due to exceptions) and more time-consuming Rule of Three.

 

 

L. Spiro

 

I agree that RAII is a concept that johnmarinelli needs to learn, but he could do without the rule of two. I made this objection earlier in the thread. You don't want a single object managing more than one resource. Also, writing a rudimentary smart pointer, then implementing different copy semantics on top of it, as link you showed suggests, is not, in my opinion, generally the best way to ensure RAII.

 

EDIT:These quote tag are very annoying to work with!


Edited by King Mir, 07 February 2013 - 08:47 PM.


#25 Strewya   Members   -  Reputation: 1334

Like
0Likes
Like

Posted 08 February 2013 - 03:31 AM

I agree that RAII is a concept that johnmarinelli needs to learn, but he could do without the rule of two. I made this objection earlier in the thread. You don't want a single object managing more than one resource. Also, writing a rudimentary smart pointer, then implementing different copy semantics on top of it, as link you showed suggests, is not, in my opinion, generally the best way to ensure RAII.

You probably don't want to manage resources in your objects at all, for that matter, but get references to them from an object pool/manager/cache of some sort that actually does the lifetime management of the resources.
But sometime it's even not about resources. Do you need to follow the rule of three if your object has a const POD member?

Also, i saw the linked article to be more about not needing the rule of three if you use smart pointers/RAII, and not about implementing your own smart pointers.

Edited by Strewya, 08 February 2013 - 03:32 AM.

devstropo.blogspot.com - Random stuff about my gamedev hobby


#26 King Mir   Members   -  Reputation: 1934

Like
0Likes
Like

Posted 08 February 2013 - 07:26 AM

But sometime it's even not about resources. Do you need to follow the rule of three if your object has a const POD member?

Good point. I recently had to implement copy and assignment operators for a class with reference members. And as you say, you don't need a destructor for that.

Also, i saw the linked article to be more about not needing the rule of three if you use smart pointers/RAII, and not about implementing your own smart pointers.

My point is one should be useing smart pointers with the right copy semantics, or a wrapper that implements copy semantics per resource.

#27 L. Spiro   Crossbones+   -  Reputation: 12966

Like
0Likes
Like

Posted 08 February 2013 - 10:11 AM

My point is one should be useing smart pointers with the right copy semantics, or a wrapper that implements copy semantics per resource.

What do you mean by that?
When you copy a smart pointer, the result is another smart pointer. The object to which they point doesn’t change, nor do you need to write different copy semantics for smart pointers of different kinds/different resources.

I think you are confusing copying the smart pointer itself and copying the object to which it points. If you copy a smart pointer you just get another smart pointer—if the object to which it points needs to also be copied it will be by its own copy semantic, not by those of the smart pointer.
If you copy the object to which it points you still have to implement copy semantics for that object if it is complex, regardless of whether or not it is a raw pointer being dereferenced or a smart pointer being dereferenced. This in itself is not related to either Rule of X.

The only thing that is related is that the smart pointer will delete the object to which it points for you automatically, eliminating the need for you to write an explicit destructor.


L. Spiro

Edited by L. Spiro, 08 February 2013 - 10:13 AM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#28 iMalc   Crossbones+   -  Reputation: 2292

Like
0Likes
Like

Posted 08 February 2013 - 01:57 PM

If you copy a smart pointer you just get another smart pointer—if the object to which it points needs to also be copied it will be by its own copy semantic, not by those of the smart pointer.

Just thought I'd throw a slight spanner in the works there by mentioning value_ptr. smile.png
http://www.mr-edd.co.uk/code/value_ptr
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

#29 King Mir   Members   -  Reputation: 1934

Like
0Likes
Like

Posted 08 February 2013 - 02:24 PM

My point is one should be useing smart pointers with the right copy semantics, or a wrapper that implements copy semantics per resource.

What do you mean by that?
When you copy a smart pointer, the result is another smart pointer. The object to which they point doesn’t change, nor do you need to write different copy semantics for smart pointers of different kinds/different resources.

I think you are confusing copying the smart pointer itself and copying the object to which it points. If you copy a smart pointer you just get another smart pointer—if the object to which it points needs to also be copied it will be by its own copy semantic, not by those of the smart pointer.
If you copy the object to which it points you still have to implement copy semantics for that object if it is complex, regardless of whether or not it is a raw pointer being dereferenced or a smart pointer being dereferenced. This in itself is not related to either Rule of X.

The only thing that is related is that the smart pointer will delete the object to which it points for you automatically, eliminating the need for you to write an explicit destructor.


L. Spiro

If you need to duplicate an object on copy, then you don't need a pointer at all.

 

A single resource should be managed by a single object. If copying the resource duplicates the resource, then that's part of managing the resource. The example of int * as a resource is contrived. Wanting resources to duplicate on copy is a rare. More often, a shared_ptr will implement the right semantics. If no smart pointer exists that does the right thing, then a single class that manges the resource and implements copy semantics is the way to go.



#30 leeor_net   Members   -  Reputation: 306

Like
0Likes
Like

Posted 25 July 2013 - 09:45 PM

 

You only need a const qualifier if you're passing by reference/pointer and you don't want the function to change the object being passed.


In addition to documenting your intent, const does have a function in this case -- it prevents you from modifying the argument inside the function body.

 

Very good point. Others have made the argument that it's about documentation. I fail to find that a useful argument. This, on the other hand, does make sense. While I'm personally in the habit of never modifying function arguments myself, I do see the value in this case of helping to identify potential mistakes before they happen.


-The Legend of Mazzeroth-Project Lead, Lead Software Architect & Engineer, General Tasks and Professional Chef.

 

http://www.lairworks.com


#31 Khatharr   Crossbones+   -  Reputation: 2937

Like
1Likes
Like

Posted 26 July 2013 - 12:30 AM

20101115002739_181_necropost.png


void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#32 Alpha_ProgDes   Crossbones+   -  Reputation: 4688

Like
0Likes
Like

Posted 26 July 2013 - 08:49 AM

First, better post ever.

 

Second, shouldn't this thread be stickied or something. It seems too good to be archived and be forgotten.

 

Three, I take it that Rule of Three, Rule of Two, and RAII are just C++ rules. Or do they apply to languages like C# and Java?


Edited by Alpha_ProgDes, 26 July 2013 - 08:50 AM.

Beginner in Game Development? Read here.
 
Super Mario Bros clone tutorial written in XNA 4.0 [MonoGame, ANX, and MonoXNA] by Scott Haley
 
If you have found any of the posts helpful, please show your appreciation by clicking the up arrow on those posts Posted Image
 
Spoiler

#33 Ravyne   Crossbones+   -  Reputation: 6990

Like
0Likes
Like

Posted 26 July 2013 - 11:25 AM

... shouldn't this thread be stickied or something. It seems too good to be archived and be forgotten.

 

Agreed that there's a lot of good stuff here. It would be nice to have a way of immortalizing things like this somehow without making them sticky; you'd soon enough overrun the front page with stickies. Its a hard problem, actually, to bubble content to the top in an effective way that also scales, even when all the content is curated. We have the same problem on MSDN, where I work. Most places, and most users, rely on search.

 

Arise thread long since dead. Haunt thy forums from which thou were created.

 

Please check the last posting date before you post.

 

Nice image! But the thread is only ~6 months buried, and Leeor was even on-topic. That's only low-level necromancy at best biggrin.png.



#34 King Mir   Members   -  Reputation: 1934

Like
0Likes
Like

Posted 26 July 2013 - 01:39 PM

Three, I take it that Rule of Three, Rule of Two, and RAII are just C++ rules. Or do they apply to languages like C# and Java?

The Rules of Two or Three are C++ specific. RAII is a more general principle that may be applied to languages like java and C#, but not as effectively, because those languages don't schedule object destruction predictably.

#35 Matt-D   Crossbones+   -  Reputation: 1429

Like
0Likes
Like

Posted 26 July 2013 - 03:36 PM

The Rule of Three is out-dated and does not play well with exceptions. The Rule of Two is much safer as it relies on RAII, which is in itself (almost) always the correct choice.
Rule of Two

 

 

L. Spiro

 

 

It's worth noting it has become significantly simpler now:

 

The Rule of Zero (which is actually a particular instance of the Single Responsibility Principle):

 

"Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership. Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators."

 

See: http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html


Edited by Matt-D, 26 July 2013 - 03:36 PM.


#36 SiCrane   Moderators   -  Reputation: 9497

Like
0Likes
Like

Posted 26 July 2013 - 03:41 PM

In addition to documenting your intent, const does have a function in this case -- it prevents you from modifying the argument inside the function body.

 
Very good point. Others have made the argument that it's about documentation. I fail to find that a useful argument. This, on the other hand, does make sense. While I'm personally in the habit of never modifying function arguments myself, I do see the value in this case of helping to identify potential mistakes before they happen.

Note that if you want to prevent modification of the function parameters in your function you don't need to put const on the parameters in the header, only in the implementation. The C++ overloading rules make T and const T the same for parameter types. So you can have foo(int a) in the function declaration in the header and foo(const int a) in the function definition. (Barring compiler bugs anyways. I've heard of one compiler that doesn't perform parameter type resolution properly.)

#37 MarkS   Prime Members   -  Reputation: 880

Like
1Likes
Like

Posted 27 July 2013 - 12:31 PM

I looked at L. Spiro's link about the Rule of Two.

I'm confused about a couple of points. What is the purpose of the reset() and swap() functions? Reset deletes the pointer then assigns the incoming pointer to the deleted pointer! O.o How does that work and under what circumstances would I use swap?

#38 leeor_net   Members   -  Reputation: 306

Like
0Likes
Like

Posted 04 August 2013 - 08:21 AM

Arise thread long since dead.

 

 I didn't think it was that old... whoops. Besides, I felt the need to respond to a valid counter point to my original argument.

 


Note that if you want to prevent modification of the function parameters in your function you don't need to put const on the parameters in the header, only in the implementation. The C++ overloading rules make T and const T the same for parameter types. So you can have foo(int a) in the function declaration in the header and foo(const int a) in the function definition. (Barring compiler bugs anyways. I've heard of one compiler that doesn't perform parameter type resolution properly.)

 

More good points.


-The Legend of Mazzeroth-Project Lead, Lead Software Architect & Engineer, General Tasks and Professional Chef.

 

http://www.lairworks.com


#39 Ravyne   Crossbones+   -  Reputation: 6990

Like
0Likes
Like

Posted 05 August 2013 - 03:07 PM

 

 

In addition to documenting your intent, const does have a function in this case -- it prevents you from modifying the argument inside the function body.

 
Very good point. Others have made the argument that it's about documentation. I fail to find that a useful argument. This, on the other hand, does make sense. While I'm personally in the habit of never modifying function arguments myself, I do see the value in this case of helping to identify potential mistakes before they happen.

 

Note that if you want to prevent modification of the function parameters in your function you don't need to put const on the parameters in the header, only in the implementation. The C++ overloading rules make T and const T the same for parameter types. So you can have foo(int a) in the function declaration in the header and foo(const int a) in the function definition. (Barring compiler bugs anyways. I've heard of one compiler that doesn't perform parameter type resolution properly.)

 

 

Adding to that, this is perfectly fine if the parameter is passed by value -- it doesn't communicate the constraint to the client, but this is fine (maybe even preferable) since it doesn't impact them.

 

But for parameters passed by reference or pointer types, its critical that const appear in the header as well, because then you need to communicate to the client that even though you're taking the parameter by ref or ptr, you are promising not to change it. I don't actually know off-hand, but passing by ref or ptr might affect the overload rules sicrane mentions (that is, are &T and const &T treated differently than T and cont T, WRT overloading), so the point in practice may be moot, enforced by the compiler anyhow. Just be aware that you need to think about how the parameters are passed in this context.



#40 SiCrane   Moderators   -  Reputation: 9497

Like
0Likes
Like

Posted 05 August 2013 - 03:32 PM

When a T is a pointer or reference then const T is not the same as adding const to the thing it refers to. Say T is U *. const T is U * const, a const pointer to non-const U, not const U *, a non-const pointer to const U. So for the purpose of overload resolution foo(U *) and foo(U * const) are the same, but different than foo(const U *).




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS