c++ swapping unique_ptr's - c++

New to this kind of stuff, probably doing something wrong, but -
I have 3 members
std::unique_ptr<Gun> currentWeapon;
std::unique_ptr<Gun> weaponSlotOne;
std::unique_ptr<Gun> weaponSlotTwo;
Gun is a base class that has other derived classes such as Pistol and SMG.
What i'm doing is setting weaponSlotOne and weaponSlotTwo to two different guns, then setting currentWeapon to the first weapon.
weaponSlotOne.reset(new DevPistol());
weaponSlotTwo.reset(new AutoDevPistol());
currentWeapon = std::move(weaponSlotOne);
and i have a switchWeapons method, that does this:
void Player::switchWeapons() {
if(currentWeapon == weaponSlotOne) {
currentWeapon = std::move(weaponSlotTwo);
}
else {
currentWeapon = std::move(weaponSlotOne);
}
}
which seems to destroy/deallocate both guns for some reason. i'm not quite sure what's going wrong.

The problem is that after calling std::move on an object, the object is in an indeterminate state, and you can't safely do anything with the object other than destroy it or assign to it.
In your case, after doing currentWeapon = std::move(weaponSlotOne);, weaponSlotOne is indeterminate, so when you test currentWeapon == weaponSlotOne you might get any result. Probably, this will be false (weaponSlotOne will be null), so you'll just copy it to currentWeapon, dropping whatever was there (deleting it).
The question is, what are you trying to do? If you want two weapons, and want to keep track of which one is current, it might make more sense to do:
std::unique_ptr<Gun> *currentWeapon;
std::unique_ptr<Gun> weaponSlotOne;
std::unique_ptr<Gun> weaponSlotTwo;
weaponSlotOne.reset(new DevPistol());
weaponSlotTwo.reset(new AutoDevPistol());
currentWeapon = &weaponSlotOne;
void Player::switchWeapons() {
if(currentWeapon == &weaponSlotOne) {
currentWeapon = &weaponSlotTwo;
}
else {
currentWeapon = &weaponSlotOne;
}
}
Or even more simply:
std::unique_ptr<Gun> weaponSlot[2];
int currentWeapon = 0;
void Player::switchWeapons() {
currentWeapon ^= 1;
}

In general, after moving from an object the moved-from object is in a valid but unspecified state. This means that you can only safely call those functions on the moved-from object which have no preconditions. For example destruction typically has no precondition. Typically neither does assigning to an object. And typically neither do const observers such as equality comparison with a non-moved from value.
In the case of std::unique_ptr, you can safely compare a moved-from value. But also note that unique_ptr has unique ownership semantics. I.e. two non-null unique_ptrs should never compare equal, because if they did, they would own the same pointer, and thus be in violation of the basic tenant of unique_ptr. But it often makes sense to compare unique_ptr with nullptr to find out if it owns a non-null pointer:
#include <cassert>
#include <memory>
int
main()
{
std::unique_ptr<int> p(new int(3));
auto p2 = std::move(p);
assert(p == nullptr); // perfectly legal & practical use of moved-from value
}
I suspect the problem with your code is/was that you were mistakenly expecting copy semantics from the unique_ptr move assignment: i.e. that the source of the assignment would be left unchanged. However from my code snippet above it can be shown that the moved-from unique_ptr will be reliably left equal to nullptr. There simply is no other way to implement the operation while still satisfying all unique_ptr specifications.

Just in case anyone ever makes it to this post. You can use the std::swap on shared or unique pointer.
http://www.cplusplus.com/reference/memory/shared_ptr/swap/
edit:
try to avoid code like:
weaponSlotOne.reset(new DevPistol());
It can cause memory leaks. Better would be:
weaponSlotOne = std::make_unique<DevPistol>();

Related

unique_ptr after move and reassigning stuff

Is it safe to do the following?
auto pt = getUniquePtr();
while (pt) {
vector_of_objects.push_back(std::move(pt));
pt = getUniquePtr();
}
getUniquePtr returns a unique_ptr<MyObject>. I believe that, after moving away the object, it shouldn't matter if I reassign something else to pt, right?
Yes, this is fine.
Moving from an object leaves it in a valid but unspecified state; all the class invariants are maintained and operations act as specified.
This means that you can rely on being able to reassign, but can't make any guarantees about the current contents of the pointer.
I'm not sure what you're worried about, but this should be absolutely safe. A unique pointer is unique. So once you've moved the pointer into the vector, pt is an empty pointer that you are free to re-assign.
It is safe, the resources owned by pt no longer belongs to 'pt' thanks to the std::move. In my debugger that pointer would be set to the special has-been-deleted address.
In your example it might be "safer" to give pt a tighter scope and reduce the duplicate getUniquePtr call, but that's pretty marginal stylistic point.
while (true) {
auto pt = getUniquePtr();
if(pt == nullptr) break;
vector_of_objects.push_back(std::move(pt));
}

inserting temporary std::shared_ptr into std::map, is it bad?

I'm designing a class for my application that implements a lot of standard shared pointers and usage of standard containers such as std::map and std::vector
It's very specific question to the problem so I just copied a piece of code
from my header for clarification purposes..
here is a snapshot of that declarations from the header:
struct Drag;
std::map<short, std::shared_ptr<Drag>> m_drag;
typedef sigc::signal<void, Drag&> signal_bet;
inline signal_bet signal_right_top();
and here is one of the functions that uses the above declarations and a temporary shared_ptr which is intended to be used not only in this function but until some late time. that means after the function returns a shared pointer should be still alive because it will be assigned at some point to another shared_ptr.
void Table::Field::on_signal_left_top(Drag& drag)
{
m_drag.insert(std::make_pair(drag.id, std::make_shared<Drag>(this))); // THIS!
auto iter = m_drag.find(drag.id);
*iter->second = drag;
iter->second->cx = 0 - iter->second->tx;
iter->second->cy = 0 - iter->second->ty;
invalidate_window();
}
the above function first insert a new shared_ptr and then assigns the values from one object into another,
What I need from your answer is to tell whether is it safe to insert temporary shared_ptr into the map and be sure that it will not be a dangling or what ever bad thing.
According to THIS website the above function is not considered safe because it would much better to write it like so:
void Table::Field::on_signal_left_top(Drag& drag)
{
std::shared_ptr pointer = std::make_shared<Drag>(this);
m_drag.insert(std::make_pair(drag.id, pointer));
auto iter = m_drag.find(drag.id);
*iter->second = drag;
// etc...
}
well one line more in the function.
is it really required to type it like that and why ?
There's no difference between the two functions in regard to the std::shared_ptr, because the std::make_pair function will create a copy of the temporary object before the temporary object is destructed. That copy will in turn be copied into the std::map, and will then itself be destructed, leaving you with a copy-of-a-copy in the map. But because the two other objects have been destructed, the reference count of the object in the map will still be one.
As for handling the return value from insert, it's very simple:
auto result = m_drag.insert(...);
if (!result.second)
{
std::cerr << "Could not insert value\n";
return;
}
auto iter = result.first;
...
The code in the example given is different from your example code, because it is using the new operator instead of std::make_shared. The key part of their advice is here:
Since function arguments are evaluated in unspecified order, it is possible for new int(2) to be evaluated first, g() second, and we may never get to the shared_ptr constructor if g throws an exception.
std::make_shared eliminates this problem - any dynamic memory allocated while constructing an object within std::make_shared will be de-allocated if anything throws. You won't need to worry about temporary std::shared_ptrs in this case.

How to safely check if the a class has been instantiated in C++?

Oh damn it I'm so stupid :-(,I could just use a bool array to reach the purpose
For example,I recieved a pointer p_class;
So how do I check if p_class points to a instantiated class?
I mean,even if the class was not been instantiated,every function of the class can still be called;and if I tried to access a variable in the class,I may access some illegal address and the program will crash
Well,I'm trying to write a network lib.
Since there are many protocols the lib have to support,
the_lib
{
public:
int selectProtocol(prtcl/*an enum*/ p)
{
switch(p)
{
case tcp:
t=new tcp;
....
}
}
int setTCP(opt,para)
{
switch(opt)
{
case csum:
t->setcsum(para)
break;
....
}
int setUDP(opt,para);
....
private:
TCP *t=0;
UDP *u=0;
...
}
Thus if the user select UDP at first but call setTCP, I have to determine whether to actually call the function in class TCP or not
0. The best thing to do? Don't use pointers.
MyClass instance;
instance.do_something(); // guaranteed to be safe to call
But if you need to refer to some object, then use references! In a well-formed piece of code, references are guaranteed to be pointing to some object, and thus any method you wish to call are guaranteed safe to be called.
But in those cases where you really need to use pointers, then:
1. Don't use raw pointers. Use smart pointers.
std::unique_ptr is almost always the best smart pointer for most of your cases. If you need shared semantics, then use std::shared_ptr. But remember: always prefer std::unique_ptr.
The good thing with most smart pointers is that they already do some default initialization for you when they are constructed without being initialized. Mostly, they set their internal pointer representation to nullptr.
2. Don't need ownership of an object? Just want to keep a reference but want to be able to set nullptr?
Use boost::optional<T&>. boost::optional has a specialization for references whose interface is very much like that of a pointer, but only much safer, as it does the same initialization technique as those of smart pointers.
3. Really need to use raw pointers for some unimaginable reason? Then always set it to nullptr when it's not meant to point to some object.
MyClass* p_instance = nullptr;
// Use p_instance, perhaps assign to it
p_instance = nullptr; // You don't need to use the pointed-to object
The gist of the above three techniques is this: because a pointer with a value of nullptr does not point to some valid object, then you can use it to check whether the pointer points to a valid object or not.
In this way your code can check if it's not initialized (p_instance == nullptr) or not (p_instance != nullptr). Also, the use of nullptr increase your chances of getting a runtime error when doing something on an invalid object, though the behavior is very much implementation dependent.
The general pattern for checking and using a pointer would be:
if(ptr) {
ptr->do_something();
}
But what if you really, really need to call do_something()? Then we are back to rule zero. :)
You can solve your problem by not using a bare pointer, and using a smart pointer instead. Both std::unique_ptr<> and std::shared_ptr<> have bool conversion operators that allow you to test if the pointer is valid or not.
class Foo { /* ... */ };
void foo (const std::unique_ptr<Foo> &p) {
if (!p) { /* ... */ }
}
void foo (const std::shared_ptr<Foo> &p) {
if (!p) { /* ... */ }
}
std::unique_ptr<Foo> p(new Foo);
std::shared_ptr<Foo> q = std::make_shared<Foo>();
If you are not performing dynamic allocation, then avoid pointers, and pass your object around to functions that take references.
void foo (const Foo &f) {
//...
}

Inside the copy constructor of shared_ptr

I have some confusion about the shared_ptr copy constructor. Please consider the following 2 lines:
It is a "constant" reference to a shared_ptr object, that is passed to the copy constructor so that another shared_ptr object is initialized.
The copy constructor is supposed to also increment a member data - "reference counter" - which is also shared among all shared_ptr objects, due to the fact that it is a reference/pointer to some integer telling each shared_ptr object how many of them are still alive.
But, if the copy constructor attempts to increment the reference counting member data, does it not "hit" the const-ness of the shared_ptr passed by reference? Or, does the copy constructor internally use the const_cast operator to temporarily remove the const-ness of the argument?
The phenomenon you're experiencing is not special to the shared pointer. Here's a typical primeval example:
struct Foo
{
int * p;
Foo() : p(new int(1)) { }
};
void f(Foo const & x) // <-- const...?!?
{
*x.p = 12; // ...but this is fine!
}
It is true that x.p has type int * const inside f, but it is not an int const * const! In other words, you cannot change x.p, but you can change *x.p.
This is essentially what's going on in the shared pointer copy constructor (where *p takes the role of the reference counter).
Although the other answers are correct, it may not be immediately apparent how they apply. What we have is something like this:
template <class T>
struct shared_ptr_internal {
T *data;
size_t refs;
};
template <class T>
class shared_ptr {
shared_ptr_internal<T> *ptr;
public:
shared_ptr(shared_ptr const &p) {
ptr = p->ptr;
++(ptr->refs);
}
// ...
};
The important point here is that the shared_ptr just contains a pointer to the structure that contains the reference count. The fact that the shared_ptr itself is const doesn't affect the object it points at (what I've called shared_ptr_internal). As such, even when/if the shared_ptr itself is const, manipulating the reference count isn't a problem (and doesn't require a const_cast or mutable either).
I should probably add that in reality, you'd probably structure the code a bit differently than this -- in particular, you'd normally put more (all?) of the code to manipulate the reference count into the shared_ptr_internal (or whatever you decide to call it) itself, instead of messing with those in the parent shared_ptr class.
You'll also typically support weak_ptrs. To do this, you have a second reference count for the number of weak_ptrs that point to the same shared_ptr_internal object. You destroy the final pointee object when the shared_ptr reference count goes to 0, but only destroy the shared_ptr_internal object when both the shared_ptr and weak_ptr reference counts go to 0.
It uses an internal pointer which doesn't inherit the contests of the argument, like:
(*const_ref.member)++;
Is valid.
the pointer is constant, but not the value pointed to.
Wow, what an eye opener this has all been! Thanks to everyone that I have been able to pin down the source of confusion to the fact that I always assumed the following ("a" contains the address of "b") were all equivalent.
int const *a = &b; // option1
const int *a = &b; // option2
int * const a = &b; // option3
But I was wrong! Only the first two options are equivalent. The third is totally different.
With option1 or option2, "a" can point to anything it wants but cannot change the contents of what it points to.
With option3, once decided what "a" points to, it cannot point to anything else. But it is free to change the contents of what it is pointing to. So, it makes sense that shared_ptr uses option3.

Nullptr and checking if a pointer points to a valid object

In a couple of my older code projects when I had never heard of smart pointers, whenever I needed to check whether the pointer still pointed to a valid object, I would always do something like this...
object * meh = new object;
if(meh)
meh->member;
Or when I needed to delete the object safely, something like this
if(meh)
{
delete meh;
meh = 0;
}
Well, now I have learned about the problems that can arise from using objects and pointers in boolean expressions both with literal numbers, the hard way :. And now I've also learned of the not so new but pretty cool feature of C++, the nullptr keyword. But now I'm curious.
I've already gone through and revised most of my code so that, for example, when deleting objects I now write
if(meh)
{
delete meh;
meh = nullptr;
}
Now I'm wondering about the boolean. When you pass just say an int into an if statement like this,
int meh;
if(meh)
Then it implicitly checks for zero without you needing to write it.
if(meh == 0) // does the exact same check
Now, will C++ do the same for pointers? If pass in a char * like this to an if statement?
char * meh;
if(meh)
Then will it implicitly compare it with nullptr? Because of how long I have been writing these ifs like this, it is second nature at this point to check if the pointers valid before using by typing if (object *) and then calling its members. If this is not the functionality why not? Too difficult to implement? Would solve some problems by removing yet another tiny way you could mess up your code.
In C, anything that's not 0 is true. So, you certainly can use:
if (ptrToObject)
ptrToObject->doSomething();
to safely dereference pointers.
C++11 changes the game a bit, nullptr_t is a type of which nullptr is an instance; the representation of nullptr_t is implementation specific. So a compiler may define nullptr_t however it wants. It need only make sure it can enforce proper restriction on the casting of a nullptr_t to different types--of which boolean is allowed--and make sure it can distinguish between a nullptr_t and 0.
So nullptr will be properly and implicitly cast to the boolean false so long as the compiler follows the C++11 language specification. And the above snippet still works.
If you delete a referenced object, nothing changes.
delete ptrToObject;
assert(ptrToObject);
ptrToObject = nullptr;
assert(!ptrToObject);
Because of how long I have been writing these ifs like this, it is second nature at this point to check if the pointers valid before using by typing if (object *) and then calling it's members.
No. Please maintain a proper graph of objects (preferably using unique/smart pointers). As pointed out, there's no way to determine if a pointer that is not nullptr points to a valid object or not. The onus is on you to maintain the lifecycle anyway.. this is why the pointer wrappers exist in the first place.
In fact, because the life-cycle of shared and weak pointers are well defined, they have syntactic sugar that lets you use them the way you want to use bare pointers, where valid pointers have a value and all others are nullptr:
Shared
#include <iostream>
#include <memory>
void report(std::shared_ptr<int> ptr)
{
if (ptr) {
std::cout << "*ptr=" << *ptr << "\n";
} else {
std::cout << "ptr is not a valid pointer.\n";
}
}
int main()
{
std::shared_ptr<int> ptr;
report(ptr);
ptr = std::make_shared<int>(7);
report(ptr);
}
Weak
#include <iostream>
#include <memory>
void observe(std::weak_ptr<int> weak)
{
if (auto observe = weak.lock()) {
std::cout << "\tobserve() able to lock weak_ptr<>, value=" << *observe << "\n";
} else {
std::cout << "\tobserve() unable to lock weak_ptr<>\n";
}
}
int main()
{
std::weak_ptr<int> weak;
std::cout << "weak_ptr<> not yet initialized\n";
observe(weak);
{
auto shared = std::make_shared<int>(42);
weak = shared;
std::cout << "weak_ptr<> initialized with shared_ptr.\n";
observe(weak);
}
std::cout << "shared_ptr<> has been destructed due to scope exit.\n";
observe(weak);
}
Now, will C++ do the same for pointers? If pass in a char * like this to an if statement?
So to answer the question: with bare pointers, no. With wrapped pointers, yes.
Wrap your pointers, folks.
It's not possible to test whether a pointer points to a valid object or not. If the pointer is not null but does not point to a valid object, then using the pointer causes undefined behaviour. To avoid this sort of error, the onus is on you to be careful with the lifetime of objects being pointed to; and the smart pointer classes help with this task.
If meh is a raw pointer then there is no difference whatsoever between if (meh) and if (meh != 0) and if (meh != nullptr). They all proceed iff the pointer is not null.
There is an implicit conversion from the literal 0 to nullptr .
It is always set a pointer to zero after invalidating it so that you know a pointer that's non-zero is valid" is an anti-pattern. What happens if you have two pointers to the same object? Setting one to zero won't be better and it does not affect the other.