Jump to content

  • Log In with Google      Sign In   
  • Create Account


C++ avoiding pointers

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

#1 martinradev   Members   -  Reputation: 143

Like
0Likes
Like

Posted 16 July 2014 - 05:23 PM

Hello,

 

I am working on a game using SDL. I was pointers to some 2d primitives, however probably to bad management there were some memory leaks. I switched to use references and not allocate dynamic memory. I believe the problems were due to not following the rule of 3 (not declaring explicitly the assignment operator).

 

So, my questions is: should I try to avoid dynamic memory allocation. For example, I know that some object is needed in the scope of a function or that it should last for a very very brief period where a few functions have to work on it. Should I in this case pass by reference or by pointer to the object?

 

What do you think?

 

To me, it seems that dynamic memory allocation is helpful only when one has to create an array where the size of the array is not known.

 

I would be grateful if you give some hints and point out when they would be definitely necessary to be used.



Sponsor:

#2 Washu   Senior Moderators   -  Reputation: 4640

Like
13Likes
Like

Posted 16 July 2014 - 05:26 PM

There is nothing wrong with using dynamic memory. In fact, for any suitably non-trivial application you will find it impossible to NOT have dynamic memory allocation.

 

If you're doing this in C++ then you should be using modern C++ methodologies, such as smart pointers.

 

Additionally, when implementing your own classes that contain raw pointers you should always be sure to implement the rule of three at the minimum, rule of five at best.


In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
ScapeCode - Blog | SlimDX


#3 SeanMiddleditch   Members   -  Reputation: 4663

Like
11Likes
Like

Posted 16 July 2014 - 06:08 PM

So, my questions is: should I try to avoid dynamic memory allocation.


When you don't need it, don't use it. When you need it, use it. C++ supports using any type as a value-type for a reason. It also supports free-store allocation for a reason. Use each where appropriate.

Roughly speaking, use value types (no allocation) when you can get away with it safely and easily. References in part are meant for this though you can also have pointer to free-store-allocated types just as easily.

The real proper use of these are all based on _lifetimes_. Who is responsible for creating the object? Who is responsible for destroying? Are users of the object just borrowing it for a while or do they need to take over responsibility of the lifetime? If they're just borrowing the object, how long do they need to borrow it for?

Value objects are created in a function's local scope and destroyed at the end of that scope. If that meets your lifetime needs then rejoice for you don't need any free-store allocation. If that does not meet your lifetime needs then you had better figure out which of the many ways of managing heap-allocated objects is best (and there's a lot of them, and all have their appropriate uses).

If you do need to use free-store, always consider std::unique_ptr as your go-to smart pointer. It's not always the right choice but it's the one you should consider first. For some concurrency data structures std::shared_ptr also works, though it and its brother std::weak_ptr are usually over-used and poorly understood and should be avoid unless you know for a solid fact that you need them and nothing else will work.

Integer handles (preferably wrapped to be type-safe) that reference an object maintained by a manager class are another good choice. In this case you replace calls to std:make_unique to something like Handle handle = myManager.createObject(parameters) and you free the objects with myManager.releaseObject(handle) and you access the actual object with MyObject* ptr = myManager.tryGetObject(handle) and remember to check for a nullptr return value (half the point of integer handles it that they can be invalidated safely, unlike pointers).

To me, it seems that dynamic memory allocation is helpful only when one has to create an array where the size of the array is not known.


This is called a std::vector and you should almost certainly never be allocating arrays like these yourself. Even if you do need a traditional stack-allocated array, consider using std::array instead of a C-style array, as it provides a number of niceties that C-style arrays do not.

#4 SmkViper   Members   -  Reputation: 440

Like
6Likes
Like

Posted 17 July 2014 - 09:08 AM

There is nothing wrong with using dynamic memory. In fact, for any suitably non-trivial application you will find it impossible to NOT have dynamic memory allocation.
 
If you're doing this in C++ then you should be using modern C++ methodologies, such as smart pointers.
 
Additionally, when implementing your own classes that contain raw pointers you should always be sure to implement the rule of three at the minimum, rule of five at best.


While understanding and following the Rule of Three/Five is important, I would suggest striving for the Rule of Zero. (Which is basically a variation on the single responsibility principle)

Edited by SmkViper, 17 July 2014 - 09:09 AM.


#5 interval   Members   -  Reputation: 126

Like
1Likes
Like

Posted 17 July 2014 - 10:07 AM

Don't avoid pointers out of fear; if you're purposely avoiding them then maybe C++ isn't for you, you might be better off using another language. You can make some pretty intense gfx games with C# (if you're writing for the Windows eco-system.) As already stated there are a number of rules-of-thumb, some tried and true patterns, and some really great new support in C++0x that make pointer management pretty easy.

 

Another thing you can do; when I was starting out with C I sat own with my compiler (and GCC or CLang is really nice for this, you can just install cywin and the gcc toolchain if you're not willing to stray from windows) and worked through some example pointer patterns until I got a fairly deep understanding of how they work. It didn't take long either.

 

If you want to be a proficient programmer with graphics you should invest the time it takes to be comfortable with pointers. Either that or just write tic-tac-toe games with other languages. Smart phone developers seem to do pretty well.

 



#6 Ravyne   Crossbones+   -  Reputation: 6908

Like
2Likes
Like

Posted 17 July 2014 - 01:26 PM

Smart pointers, smart pointers, smart pointers.

 

The whole idea of a smart pointer is that they effectively allow you to hoist dynamic, non-deterministic memory usage into the realm of static, deterministic memory usage where you can effectively treat them as value-types.

 

Today, you basically don't have to think about the Rule of 5 or rule of 3 -- provided that use an appropriate smart pointer whenever a heap object is owned, and you only use raw pointers when no ownership is implied. Which kind of pointer you use is easy once you understand what each kind of pointer does -- your entire decision-making process can be expressed through a flow-chart that fits on a postcard. The only part that's a bit tricky is learning to recognize where weak_ptr fits in, but the entire landscape is a whole lot smaller than what you need to know to implement correct memory management patterns using new/delete directly.



#7 interval   Members   -  Reputation: 126

Like
3Likes
Like

Posted 17 July 2014 - 04:18 PM

>> Smart pointers, smart pointers, smart pointers.

Sure, but then there is the philosphy that says smart pointers are over-used. One point; it's easy to avoid too much dynamic allocation through efficient design. Also, interfaces shouldn't return a smart pointer. You don't want to expose your memory management strategy to the outside to prevent the clients from making any assumption. That way lies madness. Smart pointers also step on performance, increase object contention, I could go on. Smart pointers aren't bad; they're like anything, use 'em too much and you can end up in "Bad Design Land". The ways are many, the exits few.


Edited by interval, 17 July 2014 - 04:26 PM.


#8 Andy Gainey   Members   -  Reputation: 1977

Like
4Likes
Like

Posted 17 July 2014 - 06:53 PM

 

Sure, but then there is the philosphy that says smart pointers are over-used. One point; it's easy to avoid too much dynamic allocation through efficient design. Also, interfaces shouldn't return a smart pointer. You don't want to expose your memory management strategy to the outside to prevent the clients from making any assumption. That way lies madness. Smart pointers also step on performance, increase object contention, I could go on. Smart pointers aren't bad; they're like anything, use 'em too much and you can end up in "Bad Design Land". The ways are many, the exits few.

 

Are you referring to reference-counted shared pointers specifically?  Because "smart pointers" refers to a larger collection of utilities than just shared_ptr.  unique_ptr is in fact a great default to consider before anything else, which also doesn't happen to step on performance, increase object contention, et cetera.

 

As for exposing your memory management strategy, you do want to expose your ownership model, since it is in fact a very important component of any interface.  If not documented through the code itself, you'll need to at least document it in the comments or manual.  Otherwise, users will indeed make assumptions that they ought not make.  And things such as unique_ptr, shared_ptr/weak_ptr, some mythical gc_ptr, or any other well designed smart pointer do exactly that:  They manage and communicate an ownership model (and hopefully nothing else, lest they violate the 1 responsibility principle).  unique_ptr happens to expose the memory management strategy too, for performance reasons, but you could easily write your own smart pointer that behaves like unique_ptr generally, but use a dynamic deleter more like shared_ptr allows, at the cost of performance, if you wish to hide from the consumer of your interface how memory is acquired and released.



"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke

#9 SmkViper   Members   -  Reputation: 440

Like
4Likes
Like

Posted 18 July 2014 - 07:44 AM

Also - I should mention that "avoiding pointers" is not something you can realistically do. For example, are you opening a file on disk to read a model or texture? That's a "pointer" to a non-memory resource you have to clean up at some point. Did you open a window to render to? That's also a "pointer" to a non-memory resource.

Ironically, languages without memory pointers still have these non-memory pointers somewhere and rarely have convenient mechanisms for dealing with them (see .NET's IDisposable pattern compared to C++'s deterministic destructors).

Resource management is a fact of life in programming, no matter what language you're using. So might as well learn it early smile.png

#10 SeraphLance   Members   -  Reputation: 1299

Like
0Likes
Like

Posted 18 July 2014 - 09:27 AM

You should be avoiding pointers if at all possible.  You'll know when the time comes to use them, because in those circumstances it won't be possible to do what needs to be done.  In practice, most objects are temporaries, and unless the temporary in question is ungodly large, you'll be allocating it on the stack, which means no pointers.

 

Dynamic memory allocation is useful (and required) in the following instances:

  1. Your object is too large for the stack.
  2. Your object needs to persist outside the lifetime of the allocating scope (i.e. past the '}')
  3. You want to share your object across multiple threads (usually a bad idea).
  4. You have some kind of stupid control flow requiring it, like throwing gotos around everywhere (really just a specialization of #2).

 

#2 is pretty much unavoidable in any reasonably complex program, but what's important is to recognize when it's necessary.



#11 BeerNutts   Crossbones+   -  Reputation: 2702

Like
1Likes
Like

Posted 18 July 2014 - 12:34 PM

Don't avoid pointers out of fear; if you're purposely avoiding them then maybe C++ isn't for you, you might be better off using another language. You can make some pretty intense gfx games with C# (if you're writing for the Windows eco-system.) As already stated there are a number of rules-of-thumb, some tried and true patterns, and some really great new support in C++0x that make pointer management pretty easy.

 

Another thing you can do; when I was starting out with C I sat own with my compiler (and GCC or CLang is really nice for this, you can just install cywin and the gcc toolchain if you're not willing to stray from windows) and worked through some example pointer patterns until I got a fairly deep understanding of how they work. It didn't take long either.

 

If you want to be a proficient programmer with graphics you should invest the time it takes to be comfortable with pointers. Either that or just write tic-tac-toe games with other languages. Smart phone developers seem to do pretty well.

 

Since programming in C and later moving to C++ professionally for 15 years (university and learning by myself before that), and understanding all the nuances of pointers, even I on rare occasions forget to delete/free() some pointer.  And that always causes some problem, often hard to track down ones.

 

Using smart pointers can remove that small bug that all humans are prone to create.So, it isn't about being "afraid" of pointers, it's just common sense.

 

Heck, I'm a really good driver, why would I need an air bag in my car?  I don't ever make a mistake while driving....


My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#12 interval   Members   -  Reputation: 126

Like
1Likes
Like

Posted 18 July 2014 - 08:37 PM

So, it isn't about being "afraid" of pointers...

Isn't that something you should ask the OP about? Sounds to that's exactly what he was talking about.

 

Dynamic memory allocation is useful (and required) in the following instances:
  1. Your object is too large for the stack.
  2. Your object needs to persist outside the lifetime of the allocating scope (i.e. past the '}')
  3. You want to share your object across multiple threads (usually a bad idea).
  4. You have some kind of stupid control flow requiring it, like throwing gotos around everywhere (really just a specialization of #2).

 

#2 is pretty much unavoidable in any reasonably complex program, but what's important is to recognize when it's necessary.

 

Um, 3 is often unavoidable in any useful program, and very common. that's why locks and mutexes are for. You don't share access so much as control access serially to one or more threads.



#13 Krohm   Crossbones+   -  Reputation: 3015

Like
1Likes
Like

Posted 20 July 2014 - 11:30 PM


The real proper use of these are all based on _lifetimes_. Who is responsible for creating the object? Who is responsible for destroying? Are users of the object just borrowing it for a while or do they need to take over responsibility of the lifetime? If they're just borrowing the object, how long do they need to borrow it for?
Quoted for emphasis.

I honestly find given proper design thinking about ownerships and lifetimes beats smart pointers at large.



#14 Lightness1024   Members   -  Reputation: 702

Like
0Likes
Like

Posted 22 July 2014 - 08:35 AM

 

There is nothing wrong with using dynamic memory. In fact, for any suitably non-trivial application you will find it impossible to NOT have dynamic memory allocation.
 
If you're doing this in C++ then you should be using modern C++ methodologies, such as smart pointers.
 
Additionally, when implementing your own classes that contain raw pointers you should always be sure to implement the rule of three at the minimum, rule of five at best.


While understanding and following the Rule of Three/Five is important, I would suggest striving for the Rule of Zero. (Which is basically a variation on the single responsibility principle)

 

EXACTLYYYYY finally one man on earth understands programming. damn. why is it so hard to make people forget about those damned rule of 3/5 ?



#15 SmkViper   Members   -  Reputation: 440

Like
0Likes
Like

Posted 22 July 2014 - 10:18 AM

There is nothing wrong with using dynamic memory. In fact, for any suitably non-trivial application you will find it impossible to NOT have dynamic memory allocation.
 
If you're doing this in C++ then you should be using modern C++ methodologies, such as smart pointers.
 
Additionally, when implementing your own classes that contain raw pointers you should always be sure to implement the rule of three at the minimum, rule of five at best.


While understanding and following the Rule of Three/Five is important, I would suggest striving for the Rule of Zero. (Which is basically a variation on the single responsibility principle)

EXACTLYYYYY finally one man on earth understands programming. damn. why is it so hard to make people forget about those damned rule of 3/5 ?


Because it's still important to know, especially if (when) you write your own resource management classes.

#16 Washu   Senior Moderators   -  Reputation: 4640

Like
1Likes
Like

Posted 25 July 2014 - 11:40 PM

EXACTLYYYYY finally one man on earth understands programming. damn. why is it so hard to make people forget about those damned rule of 3/5 ?


Not knowing the rule of 3/5 is like not knowing for loops. You have a giant gaping hole in your knowledge just waiting to come along and bite you on the ass.

There are a great many situations where the "rule of zero" will not work conveniently without you implementing the appropriate containers or proxies (which need to obey the rule of 3/5, by the way) in order to have your "rule of zero." Two examples where you need non-trivial handling of objects where the standard library is lacking convenient containers: handle duplication and COM with proper reference handling, unlike most DirectX code.

Edited by Washu, 25 July 2014 - 11:40 PM.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
ScapeCode - Blog | SlimDX


#17 Jason Z   Crossbones+   -  Reputation: 4846

Like
2Likes
Like

Posted 26 July 2014 - 07:33 AM

 

EXACTLYYYYY finally one man on earth understands programming. damn. why is it so hard to make people forget about those damned rule of 3/5 ?


Not knowing the rule of 3/5 is like not knowing for loops. You have a giant gaping hole in your knowledge just waiting to come along and bite you on the ass.

There are a great many situations where the "rule of zero" will not work conveniently without you implementing the appropriate containers or proxies (which need to obey the rule of 3/5, by the way) in order to have your "rule of zero." Two examples where you need non-trivial handling of objects where the standard library is lacking convenient containers: handle duplication and COM with proper reference handling, unlike most DirectX code.

 

This, plus the rule of zero depends on your compiler being able to automatically generate appropriate default equivalents for move ctor/assignment operator.  That isn't available everywhere just yet (although the situation seems to be improving).  Many people will be using older compilers, so you can't blindly forget about the rule of 3/5.







PARTNERS