Sign in to follow this  

c++ smart pointer question

This topic is 1186 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Totally a minor problem here, but I thought I would ask for help in explaining smart pointers a little.

I'm trying to get a better handle on pointers and c++11 smart pointers, but I think I'm misunderstanding smart pointers a little. Most of the references I've come across haven't been of too much use, as they're mostly geared toward a more experienced programmer, I feel. I understand the auto-deletion of unique_ptrs when they go out of scope, and why that's desirable. But, I'm having quite a bit of difficulty  using them the same way as a normal pointer.

 

Typically, one could make a pointer and have it equal a reference object like:

int firstNumber = 10;
int *pointer1;

pointer1 = &firstNumber;//*pointer1 is the value located at memory of firstNumber.

I've been trying to get the same functionality with  std::unique_ptr and am failing to really understand how to initialize them and use them.

 

Trying:

int firstNumber = 10;
std::unique_ptr < int > pointer1;
pointer1 = &firstNumber;

results in an error. |459|error: no match for ‘operator=’ (operand types are ‘std::unique_ptr<int>’ and ‘int*’)|

Fair enough. But, if I change that to:

int firstNumber = 10;
//std::unique_ptr< int > pointer1(new int(&firstNumber));//results in error
//std::unique_ptr< int > pointer1(new int(firstNumber));//doesn't seem to reference memory at firstNumber
std::unique_ptr< int > pointer1(new int(10));

pointer1 = &firstNumber;//results in same error,

*pointer1 = firstNumber//doesn't reference memory at firstNumber.
*pointer1 = &firstNumber//results in error

I've tried:

int firstNumber = 10;
std::unique_ptr < int > pointer1(new int(10));
std::unique_ptr < int > pointer2(new int(20));

*pointer2 = firstNumber;
pointer1 = pointer2;//results in error
pointer1 = *pointer2;//does change pointer 1 but doesn't seem to reference firstNumber, nor memory at pointer2

I'm feeling a little on the dumb side for not grasping this, but I'm obviously missing something(or maybe many things).

I'm apparently doing something wrong, but it feels like the unique_ptr isn't actually behaving like a pointer at all, functionality-wise.

 

Anyhow, any advice or help in understanding the functionality of unique_ptrs would be extremely appreciated. Feel absolutely free to dumb things down, I will not feel patronized or condescended to in the slightest :)

 

Thanks in advance, :D

Share this post


Link to post
Share on other sites

The smart pointers are about managing the ownership of a resource. If you store an object within an std::unique_ptr, you're telling the smart pointer that it has exclusive ownership of the object and is responsible cleaning it up.

 

But the problem is that you're trying to hand it an object, an integer variable, with automatic storage to a smart pointer that is supposed to claim ownership of it. The integer variable is already exclusively owned and managed by the surrounding scope (function body, for loop, whatever scope it is defined in) and you cannot transfer that ownership to the smart pointer.

 

Smart pointers are not arbitrary replacements for naked pointers; they are tools for managing ownership of objects. If you're not allocating objects dynamically, you most likely don't need any smart pointer at all for the object because if it is not dynamic then it is probably statically allocated or has automatic storage.

Share this post


Link to post
Share on other sites

First off, smart pointers are not meant for managing items on the stack. They are meant for managing items on the heap.  They are designed to change

{
int *foo = new int(10);
delete foo;
}

into

{
  std::unique_ptr<int> foo(new int(10));
}

notice how unique pointer removes the need to remember the matching call to delete?

 

That also means that it's always going to delete it's input, so attempting to feed it a pointer off the stack will cause it to crash on destruction:

{
  int num = 5;
  std::unique_ptr<int> ptr1(&num);
} // <-- crash

Secondly, each smart pointer type has it's own semantics about how it manages memory.  unique_ptr in particular is designed to give a single point of code ownership of an object on the heap.  That means that a unique pointer must transfer ownership, and may not be a copyed. So if you did want to transfer ownership you need to use move semantics

std::unique_ptr<int> p1(new int(10));
std::unique_ptr<int> p2(std::move(p1));

which will result in p1 being empty, and p2 containing the integer valued 10.

Share this post


Link to post
Share on other sites

Thanks for the lightning quick replies everyone. That clears up a ton of questions I had and answers my initial questions perfectly (though, I still have a lot of reading to do on the subject). I was definitely thinking of smart pointers simply as safer replacements to raw pointers.

 

I'm going to admit, I'm still a little fuzzy on when exactly I would need to use a pointer (of either sort). Even in my more complex games, with dozens of classes and vectors of classes interacting with each other, I seem to be able to work around them just fine. I'm mostly trying to learn more about them to figure out when exactly it would be appropriate and necessary (or even just more efficient) to use them. I'm happy enough to avoid them, but I'd prefer to avoid them knowing why I'm not using them, rather than out of ignorance (my current avoidance is more of a crutch than a purposeful use of the alternative methods).

 

My current reason for wanting to understand pointers a little more, is that in my current project, I have a vector of objects that are being displayed. I sort the objects for rendering as they're billboarded images. However, I need to go through the objects each turn when a button is pressed (ala the civilization games, where after giving orders to one object, the camera moves to the next player object to give orders to). But, having sorted these objects based on camera distance each iteration, it's difficult (for me or the program) to tell which object is which any longer without some pretty wonky workarounds. I'd prefer not to have two vectors, one for each player, as it makes sorting a little more complicated, though I'm sure still doable. I thought perhaps this would be an appropriate use for pointers, since regardless of where in the vector the object was moved to, the values should still be the same when referenced (i could definitely be wrong here, I haven't tested it. It may be that when sorting a vector, it just moves the values and not the objects). I can totally find a workaround for this without using pointers(my current method is to just use a second vector of ints, and when sorting, just sort that vector according to the distance of the actual vector of objects, then when rendering, render the actual vector of objects in the order of the sorted second vector. This probably isnt' ideal :P), I just thought perhaps it would be simpler and clearer to use them here. If you have any advice for this current little problem, I'd be happy to hear it, but this is definitely a second question and I'm happy to make an additional thread if needed.

 

Though, don't feel obligated to answer any follow-up questions, there are plenty of other similar threads I can look up on that (though, of the ones I've read so far, I still seem a little confused on them). Thanks for the help everyone, I genuinely appreciate it! smile.png

Edited by Misantes

Share this post


Link to post
Share on other sites

Thanks for the lightning quick replies everyone. That clears up a ton of questions I had and answers my initial questions perfectly. I was definitely thinking of smart pointers simply as safer replacements to raw pointers.

They are safer replacement, but when there's no risk there's no reason to go safer. What they are not, however, are arbitrary replacements. That is, don't just replace your pointers, but replace them when needed to eliminate risk of resource leakage. Now, "when needed" is about learning about ownership in general and how the different smart pointers work. That comes with experience.

 

I'm going to admit, I'm still a little fuzzy on when exactly I would need to use a pointer (of either sort). Even in my more complex games, with dozens of classes and vectors of classes interacting with each other, I seem to be able to work around them just fine. I'm mostly trying to learn more about them to figure out when exactly it would be appropriate and necessary (or even just more efficient) to use them. I'm happy enough to avoid them, but I'd prefer to avoid them knowing why I'm not using them, rather than out of ignorance (my current avoidance is more of a crutch than a purposeful use of the alternative methods).

It is perfectly fine and possible to write complex software (with some definition of complex, of course) without explicitly touching dynamic allocations. You can do dynamic arrays with vectors for example, and the vector will safely handle its ownership by itself.

 

Once you start allocating "dumb" resources, for example allocating objects with new or some other function that gives you, the programmer, the responsibility of releasing said resource, you can/should look into smart pointers. But if there's no ownership you have to manage, or on other words; you're not given the responsibility to clean it up by yourself, then there's no reason for smart pointers either.

 

Take KulSeran's post above as an example. The first code box allocates a dumb resource and you have to call delete on the pointer. That's a perfect place for a smart pointer as shown in the second code box. The third code box doesn't allocate anything dynamically, the integer has automatic storage and nothing has to be cleaned up. There's no need for a smart pointer here because, as said, there's nothing to clean up.

Edited by Brother Bob

Share this post


Link to post
Share on other sites


(i could definitely be wrong here, I haven't tested it. It may be that when sorting a vector, it just moves the values and not the objects)

Yeah... That's totally wrong.  C++, unlike other languages like Java has a really bare-bones notion of what an "object" is.

vector<int> numbers;
numbers.push_back(20);
numbers.push_back(10);

int *n1 = &numbers[0];
int *n2 = &numbers[1];

std::sort(numbers.begin(), numbers.end());
std::cout << *n1 << " " << *n2 << std::endl;

// NOTE: after this line, n1 and n2 may no longer point to valid memory!!!
numbers.push_back(30);

Pointers point to a memory locations, not "objects".  In this case a vector has a pointer to a contiguous block of memory that it uses for storage.  The pointers n1 and n2 point into that storage.  Sorting the vector moves around values within that storage, and now your pointers are pointing to different numbers because that is the data now stored at that memory location.  Then, to my final note, adding more objects to a vector can cause it to copy it's content to totally different location in memory, leaving n1 and n2 pointing into nothingness/undefined-bahavior land.

Share this post


Link to post
Share on other sites

Speaking of pointers (and references) in general, when not pointing at dynamic memory:

Even in my more complex games, with dozens of classes and vectors of classes interacting with each other, I seem to be able to work around them just fine.
[...]
I'm happy enough to avoid them, but I'd prefer to avoid them knowing why I'm not using them

Pointers shouldn't be avoided, but they shouldn't be used just for the sake of using them.

By default, a variable shouldn't be a pointer; it's not a matter of knowing when not to use them, but knowing when to use them.
 

I'm going to admit, I'm still a little fuzzy on when exactly I would need to use a pointer (of either sort).
[...]
I'm mostly trying to learn more about them to figure out when exactly it would be appropriate and necessary (or even just more efficient) to use them.


Here are three situations where you might want a reference or pointer:
Usage #1: You use a pointer or reference when you need more than one variable to point to the same data (either for read-only or read-write usage), rather than trying to keep two or more variables in-sync with each other.
Usage #2: You also use a pointer/reference when you need to pass around a large variable to functions, but you don't want for to copy that memory when you aren't actually needing a copy.
Usage #3: Another use of pointers and references is when you want a function to modify an existing variable.

(There are other situations as well, these are just three common ones)
 

struct ButtonAppearance
{
   Font font; //Font for the button.
   Texture background; //Appearance of the button.
   Color defaultFontColor;
};

class Button
{
public:
   Button(const ButtonAppearance &sharedButtonData)
      : sharedButtonData(sharedButtonData)
   {    }
   
   //We don't want to COPY the screen, we want to modify the original screen. (See Usage #3 above)
   //So we use a non-const reference.
   void DrawTo(Screen &screen) const
   {
      //Draw the button background on the screen.
      DrawTexture(screen, sharedButtonData.background);
      
      //Draw the button text, using the shared font, but the local text color and the local text.
      DrawText(screen, sharedButtonData.font, textColor, text);
   }

private:
    const ButtonAppearance &sharedButtonData; //Const references are read-only.
    Color textColor;
    std::string text;
};

//We don't want to modify OR copy a huge number of buttons. We want to draw them in a way that doesn't
//change the buttons themselves (only changes the screen). So we pass the buttons by const reference. (See Usage #2)
void DrawAllTheButtons(const std::vector<Button> &buttons, Screen &screen)
{
   for(...every button...)
   {
      buttons[0].DrawTo(screen); //We don't want to copy the screen, only modify the original screen.
   }
}

int main()
{
   ButtonAppearance buttonAppearance;

   std::vector<Button> alotOfButtons;
   for(...however many...)
   {
      //Every button is sharing the 'buttonAppearance' data, instead of copying it. (See Usage #1)
      Button newButton(buttonAppearance);
      alotOfButtons.push_back(newButton);
   }

   Screen screen;
   DrawAllTheButtons(alotOfButtons, screen);
   
   return 0;
}

Passing by reference or pointer is roughly the same in size and performance as passing a large integer. This is instead of passing thousands of bytes of data for large arrays of data.

 

[Edit:] I should mention, smart pointers aren't just for extra safety (preventing human programmers from making really common mistakes), they also also for self-documenting code. Smart pointers show the intent of the code, both to yourself when you need to read or modify the code a few months later, and to others reading your code. They document the intent when used in a consistent way; they don't actually enforce intent 100%, because, while they use compile-time errors to say, "hey, you are accidentally violating your agreements to be read-only (const) or to be the sole-owner (unique_ptr)", you can actually tell the compiler to allow you to violate the intent (by, for example, removing 'const' from a variable in certain functions, or by manipulating the unique_ptr to take ownership from it).

 

So smart pointers are also about providing tools for programmers to make their code be less mistake-prone by allowing programmers to make promises that, yes, can be broken, but that are less likely to be broken by accident. These promises can be read by the compiler so it can make better optimizations, as well as read and comprehended by other programmers so they can make more informed decisions when editing code.

 

Smart pointers show greater intent (reveal more information) than raw pointers do.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

Pointers point to a memory locations, not "objects".  In this case a vector has a pointer to a contiguous block of memory that it uses for storage.  The pointers n1 and n2 point into that storage.  Sorting the vector moves around values within that storage, and now your pointers are pointing to different numbers because that is the data now stored at that memory location.  Then, to my final note, adding more objects to a vector can cause it to copy it's content to totally different location in memory, leaving n1 and n2 pointing into nothingness/undefined-bahavior land.

Ah, I had a feeling it may work this way. I just wasn't certain if the memory location would be moved with the sorting or not. You explanation clears that up perfectly.

 

 

edit* for whatever reason, the selective quote makes your quote empty, and posts it as my text :P iono. Here's your quote:

 

"Here are three situations where you might want a reference or pointer:
Usage #1: You use a pointer or reference when you need more than one variable to point to the same data (either for read-only or read-write usage), rather than trying to keep two or more variables in-sync with each other.
Usage #2: You also use a pointer/reference when you need to pass around a large variable to functions, but you don't want for to copy that memory when you aren't actually needing a copy.
Usage #3: Another use of pointers and references is when you want a function to modify an existing variable."

//end Servant's quote :P


Ok. I use the second and third examples pretty liberally already(at least the reference side of things). I guess I just wasn't semantically considering that the use of "pointers."  The first is one I don't often use, and probably the use I should look into understanding better.

 

These responses are leading me to think that perhaps I'm overcomplicating things, and feeling as though I probably ought to be using pointers more often (or at least missing out by not using them), when in fact, I'm already using them (more or less) correctly as is. I definitely have some reading to do (especially the whole stack/heap thing. I have a rudimentary understanding of the difference, but don't really understand the details of them).

 

Thanks for the responses everyone. smile.png I definitely now still have questions about sorting multiple vectors together, but I'll make a new thread for it.

Edited by Misantes

Share this post


Link to post
Share on other sites

This topic is 1186 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this