I am a beginner to c++, I was learning the concept of shared_ptr.
I also understood that
Several shared_ptr objects may own the same object and the object is destroyed and its memory deallocated when either of the following happens:
1.the last remaining shared_ptr owning the object is destroyed;
2.the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
But when I tried to execute a sample program
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area()
{
return length * breadth;
}
};
int main()
{
shared_ptr<Rectangle> P1(new Rectangle(10, 5));
cout << P1->area() << endl;
shared_ptr<Rectangle> P2;
P2 = P1; //how is this possible
// This'll print 50
cout << P2->area() << endl;
// This'll now not give an error,
cout << P1->area() << endl;
cout << P1.use_count() << endl;
return 0;
}
After "P2=P1" the memory allocated to P1 has to be deallocated right?
But still we can print P1->area().
Please explain how this is happening?
2.the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
Yes.
After "P2=P1" the memory allocated to P1 has to be deallocated right?
No. It could happen to P2 if it pointed to something. Not P1.
The logic behind the rule (2) is that the assignment overwrites the value of the first operand, so the first operand will no longer point to what it used to. If it was the last pointer to something, then nothing will point to that anymore, and it can be deleted.
What you learned about destruction of shared pointers is correct. But here, you are not destroying P1. Instead, you are assigning P1 to P2. The implementation of shared_ptr has an overloaded copy assignment operator allowing for this operation and making it correct.
Through that overloaded implementation, P2 is now a shared pointer pointing to the same object as P1 - both pointers access the same object, so you are printing the same areas. Both of them exist in a valid state and as you see, the count of pointers that manage that Rectangle object is 2.
Defining the overloaded = is aligned with the concept of shared_ptr - there are multiple pointers pointing to (owing) the same object. If you want to see a contrasting implementation, look up the unique_ptr - this is a smart pointer that assumes only one pointer has an ownership of the object. It also has an overloaded assignment operator, but using it would invalidate P1 (to my knowledge it would set it to nullptr so it would still be in a valid state, just not pointing to anything). P2 would be the only owner of the Rectangle. Worth trying, for better understanding.
You can find more on the shared_ptr functionality here.
Related
The difference between smart pointers and raw pointers has been discussed before (e.g. When should I use raw pointers over smart pointers?), but I can't quite get the answer to this question from the material I have been reading the last day or so.
I have a class A that has a pointer int* a to some data. In the context I am thinking about, the value that a points to might be used somewhere else in the program so A does not have ownership over a, it just refers to it. For example, a house exists (e.g. int h) and a person (i.e. class) has a reference to their house (e.g. int* my_h).
The first way I handled this was without using smart pointers, but I am curious about the benefits of using smart pointers in this example. I suspect there are not many because ownership really isn't much of an issue and I am not calling new and delete.
The example with raw pointers:
#include<iostream>
class A{
public:
A(int a_val){
std::cout << "Creating A instance ";
a = &a_val;
std::cout << "with a = " << *a << std::endl;
};
private:
int* a;
};
int main()
{
int x = 5;
std::cout << "x is " << x << std::endl;
A a(x);
return 0;
}
Here, a has the raw pointer int* a, which is assigned to &x in main().
The example with smart pointers (a unique_ptr):
#include<memory>
#include<iostream>
class A{
public:
A(std::unique_ptr<int> a_val){
std::cout << "Creating A instance ";
a = std::move(a_val);
std::cout << "with a = " << *a << std::endl;
};
private:
std::unique_ptr<int> a;
};
int main()
{
std::unique_ptr<int> x = std::make_unique<int> (5);//int x = 5;
std::cout << "x is " << *x << std::endl;
A a(std::move(x));
return 0;
}
The use of unique_ptr seems overkill here to me, and doesn't benefit readability or performance. Is that correct?
EDIT
As pointed out (...) in the comments, there were a number of problems with the original example. The raw pointer example should be:
#include<iostream>
class A{
public:
A(int* a_val){
std::cout << "Creating A instance ";
a = a_val;
std::cout << "with a = " << *a << std::endl;
};
private:
int* a;
};
int main()
{
int x = 5;
std::cout << "x is " << x << std::endl;
A a(&x);
return 0;
}
and the smart pointer example should perhaps use a shared_ptr.
To be more specific, I am interested in cases where this scales up to large numbers of instances of classes, which have pointers (or vectors of pointers) to data structures defined elsewhere. For instance, in agent based models, agents sometimes need to 'find' another agent, and thus a vector of pointers could (in my understanding) be used to refer to which other agents one particular agents should 'know about'. The agents themselves will be created outside of the agent class.
As was pointed out by #YKsisarvinen in a comment,
One difference is that your raw pointer version doesn't work. You assign a pointer to a temporary variable, which dies as soon as constructor is done. Which highlights a very important difference between raw and smart pointers - smart pointers prevent you from most of such mistakes.
One possible approach is to store a reference to the object that is owned by somebody else.
struct A
{
A(int& a_val) : a(a_val) {}
int& a;
};
However, that would prevent you from being able to assign objects.
int i = 10;
int j = 20;
A a1(i);
A a2(j);
a1 = a2; // This will be an error.
You can use std::reference_wrapper to overcome that barrier.
struct A
{
A(int& a_val) : a(a_val) {}
std::refernce_wrapper<int> a;
};
I am curious about the benefits of using smart pointers in this example
Depends on the smart pointer you are talking about:
unique_ptr: you cannot use this one for your use case, because that implies you own the object.
shared_ptr: this would work, but you would need to use shared_ptr everywhere and you would tie its lifetime to your class.
weak_ptr: same as above, but this would not tie the lifetime (it can be destroyed by others meanwhile).
Some other non-standard smart pointer: depends.
In general: if you only need to keep a reference to an object, use a reference or a pointer. Of course, you will have to ensure the reference/pointer remains valid, which can be trivial or a nightmare, depending on the design of your program.
The main question is: who's owning the pointer. In this example:
int main()
{
std::unique_ptr<int> x = std::make_unique<int> (5);//int x = 5;
std::cout << "x is " << *x << std::endl;
A a(std::move(x));
return 0;
}
It's just the same.
But if you do something like:
A funct(int n)
{
std::unique_ptr<int> x = std::make_unique<int> (n);
std::cout << "x is " << *x << std::endl;
A a(x.get());
return a;
}
Then you NEED to transfer the ownership, because as soon as you leave funct, x goes out of scope and gets destroyed. And the A object that you return now points to garbage. So you'll do:
A funct(int n)
{
std::unique_ptr<int> x = std::make_unique<int> (n);
std::cout << "x is " << *x << std::endl;
return A(std::move(x));
}
So, the A returned now owns the pointer and will destroy it when itself gets destroyed.
Even before jumping into the benefits of Smart Pointers, I would recommend understanding why are they even needed. First and foremost, smart pointers were never meant to replace the use of all Raw pointers. They were designed with a very very specific purpose: Memory management i.e. cleaning up/freeing dynamically allocated memory.
Now lets say we have a simple function as follows:
1. void func()
2. {
3. SomeResource* raw_sr = new SomeResource(); //--->Memory allocated here.
4. .
5. .
6. if(someCondition==false)
7. return;
8.
9. delete raw_sr;
10. return;
11.}
The above function can terminate in 2 ways:
1. Returning from Line 10. -->Normal termination
2. Returning from Line 7. -->Termination under some error condition
In the first situation we cleaned up the memory.
But for situation 2, we have a potential memory leak as we did not free the raw_sr.
So apparently, there is only one place where the memory was not deleted.
We can add an explicit code there to free up the raw_sr memory.
But the point is, there can be a number of situations in the code, where this can happen and forget to free up the memory. It can also be while handling exceptions.
But what if, under all terminating conditions we are guaranteed that the memory will be freed up ?
There is only one function that is called under all such circumstances: the destructor of a static object.(Not dynamic - very imp.)
So the idea is to give the responsibility of holding a pointer to a specific class whose job will be to free up the memory held by the pointer when its destructor is called.
This class is called a Smart Pointer.
So now your code would look like this:
1. void func()
2. {
3. SmartPointer<SomeResource> smart_sr(new SomeResource()); //--->Memory allocated here
4. . // but passed onto the smart_sr obj.
5. .
6. if(someCondition==false)
7. return;
8.
9. return;
10.}
Now under all circumstances, be it on line no 7 or 10, the destructor of smart_sr will be called and that will free up the memory held for SomeResource.
This is the primary and fundamental understanding of a Smart Pointer.
Hence, usage of a smart pointer depends on its usage context.
Now based n your example, Class A represents a Person that holds int* my_house.
Now lets say you are not the only person staying in this house. You are staying with your wife.
From a logical perspective your object and your wife's object should point to the same House pointer.
So your object alone CANNOT be the owner of this house's pointer. Because then your Wife's object wont be able to refer to it.
So std::unique_ptr cannot be used for holding the house pointer.
Well you guys share the home ..right ?
Hence, std::shared_ptr, will make sense.
Now lets say, your wife left, the home(for some reason) so she gives up the pointer. Will the house be returned back to its owner ? No. You are still staying there. Subsequently, you move out too after a few days, then you too will give up this pointer. And because you were the last person staying in it, the memory holding this house will finally be freed.
Hope this clears your doubt and puts you in the right direction to understanding smart pointers.
To my understanding of the subject:
A shallow copy is when the non-pointer types of an object are copied to another object. Shallow copies can't be done when an object has pointers because, the object being copied will get the address of that pointer, and when either of the two objects are deleted, the other will be left dangling since they were pointing to the same location in memory.
A deep copy is used when pointers are involved because it reserves a space separate from the original object's space and just copies the contents from one memory location to another. This way, when one of the objects is deleted, teh other isn't left dangling.
That said, I would like to know why this program is working even though I've done a shallow copy of a pointer
struct aStruct {
int *i;
aStruct(int *p) : i(p) {
cout << "Created aStruct" << endl;
}
aStruct(const aStruct &s) {
cout << "Copying aStruct" << endl;
i = s.i;
}
aStruct &operator=(const aStruct &s) {
cout << "Assigning aStruct" << endl;
i = s.i;
return *this;
}
};
int main() {
int *x = new int(3);
aStruct s1(x);
aStruct s2 = s1;
int *y = new int(4);
aStruct s3(y);
s3 = s1;
}
s1, s2, and s3 all have their variable i pointing to the same place. So when the end of the main() function is reached and one of them is destroyed, shouldn't the others be left dangling causing an error? My program works fine. Could someone please be kind enough to explain this to me?
Thanks all
You are copying the pointer, not the data. Each object here is an object in its own right, additionally, you seem to be under the impression that C++ is garbage-collected. it is not (except in some uncommon implementations)
Your program basically leaks memory and is only cleaned up by virtue of the OS releasing whatever your process consumed after it terminated. Consequently, all your pointers are pointing to perfectly valid memory throughout the lifetime of your application.
Unfortunately, there is no concept of Deep and Shallow pointers in C++. The notion of references in Java and C# is different from pointers in C++.
You should read on
What are the differences between a pointer variable and a reference variable in C++?
What is a smart pointer and when should I use one?
What is The Rule of Three?
Take it this way, in C++, Pointers refers to whatever object is in the memory location it, the pointer is pointing to.
So, what you are doing is copying the allocated location into your constructor into the object... When the destructor runs, of cause the entire object is freed from memory (dead), including the pointer int* i that is a data member. But the memory location allocated by new isn't freed until someone calls that same location with a delete.
I understand how to move an object, for example:
int a(5), b(6);
int c(std::move(a)); // move constructor
c = std::move(b); // move assignment
I understand the common implementation of a custom move constructor (which takes the ownership of a dynamic pointer and sets the moved-from pointer to nullptr.)
But I haven't found anything about moving a dynamically allocated pointer itself. I need confirmation that the following code is legal:
int *p(new int(42));
int val(std::move(*p));
delete p; // I think this is no longer necessary. Is it?
So, is it allowed to move a dynamic pointer?
std::move does not move anything. It merely casts an l-value reference to an r-value reference.
In the example you give, the pointer p has not moved anywhere.
BTW. please don't deal in raw pointers - use a std::unique_ptr instead.
This allocates memory for an int, initialises the int to the value of 42 and stores the address of that memory in p.
int *p(new int(42));
This casts the int pointed to by p to an int&& and then constructs the int val from that r-value reference. Since int is an integral type, a construction from an r-value (i.e. a move) is equivalent to a copy (this is mandated in the standard)
int val(std::move(*p));
Yes, this is still necessary.
delete p;// i think this is no longer necessary. is it ?
In your second example, your code essentially copy the value of one integer to anther. You are not moving the pointer, you are moving the integer the pointer points to.
int a = 42;
int b = std::move(a);
std::cout << a << " : " << b << std::endl; // prints 42 : 42
Moving an integer is copying them.
Even if you moved the pointer, the value of the pointer of copied:
int* a = new int{42};
int* b = std::move(a);
std::cout << *a << " : " << *b << std::endl; // prints 42 : 42
std::cout << std::boolalpha << (a == b) << std::endl; // prints true
So moving raw pointers are copying them too.
You will still need to delete the p pointer.
For every new, there's a delete.
If you don't want to delete it yourself, consider std::unique_ptr, which is aware about move semantics.
Built-in type such as raw pointer, int, floats are not aware of move semantics and moving them will simply copying them.
In your example the use of move semantics is trivial, since you are only using primitive types and raw pointers. std::move is really doing nothing.
Move semantics make the difference when your objects belong to a class that supports move operations (move constructor, move assignment, etc...), or interact with other classes/ functions that do so.
Tipically, after you have move from an object (which is not the result of std::move itself, but the result of the subsequent call), the object is still alive and in a valid state, although it has been deprived of any managed content. Distruction of the objects still remains to be done, it's still a living object.
Note that you rarely see move semantics with raw pointers. It is common to see it with automatic storage objects, or smart pointers. And that's just because is the scenario where move semantics play well with.
In this code I tried to make a template classP_array which store pointer to an object as shown below. Simply the template class stores pointers and deletes them if not deleted after usage. So I should do a NULL check.
Although this problem was addressed in other questions. I don't really understand how to check if pointer is NULL or not.
In the Destructor ~P_array() checking NULL pointers is not effective.
I tried something similar using std::shared_ptr to explain the idea.
p_array destructor:
~P_array() {
unsigned int i = 0;
while(i < total)
{
if (t_array[i]) //Not effective
{
std::cout << "deleting " << t_array[i] << "\n" ;
delete (t_array[i]); //Ownership of pointer
}
i++;
}
}
main.cpp :
#include <iostream>
#include <memory>
#include "p_array.h"
class Obj {
public: ~Obj () { std::cout << "Deleting Obj...\n" ;}
};
int main() {
P_array<Obj> ap;
Obj * op[10];
for (int i = 0; i < 10 ; i++)
{
op[i] = new (Obj);
ap.add(op[i]);
}
std::shared_ptr <Obj> sp = std::make_shared<Obj>();
//if (sp.get()) delete sp.get() ; // invalid pointer error
sp.reset();
//delete op[0] ;// gives double free error
}
So simply my question is:
If I delete a pointer which is stored in a container which deletes the pointers in the destructor then how the container to know if these pointers are actually deleted?
Does deleting a pointer makes it a NULL pointer?
If the container can't check for deleted pointers then how can it gain the ownership of these pointers such as in the case of the P_array example?
If I delete a pointer which is stored in a container which delete the pointers in the destructor then how the container knows if these pointers are accually deleted?
It doesn't. A pointer has no information beyond the address of the object it points to. If you need extra information to determine whether that object was dynamically allocated, or whether it's been deleted, you'll need to keep that somewhere else, like in a smart pointer.
Does deleting a pointer makes it a NULL pointer ?
No.
if the container can't check for deleted pointers then how it can gain the ownership of these pointers such in case of P_array example
Use a smart pointer like std::unique_ptr or std::shared_ptr, depending on whether it should have exclusive or shared ownership.
If I delete a pointer which is stored in a container which delete the
pointers in the destructor then how the container knows if these
pointers are accually deleted?
A well accepted programming behaviour is to
initialize all pointers when defining
setting pointers to NULL if you are deleting the object it is pointing
always check if the pointer is not NULL before dereferencing
This trivial programming discipline would ensure
Double delete will not occur
Dangling pointers would not cause a crash
Dereferencing invalid pointer would not crash your program
Of-course all the above problems can be inherently handled if you use smart pointers. The exact smart pointer that should be used will depend on your design.
If the container owns the pointers, then it owns the pointers. Just don't delete them external to the container. That's the container's job! Any external deletion is just incorrect. Note that this code is syntactically correct, just wrong:
{
std::unique_ptr<int> up(new int(42));
delete up.get();
// oops, now deleted again
}
It's up to the programmer to know not to do that.
No. It just frees the memory for use later, it doesn't have to touch the pointer or zero out the memory it points to or anything like that.
That is part of the contract of the class. It just owns the pointers. Period. The destructor should unconditionally delete its pointers, unless you expose a way to release() (to borrow unique_ptr's method) them.
Note that in C++11, you could just implement this simply as:
template <typename T>
using P_Array = std::vector<std::unique_ptr<T>>
There are other suggestions of using std::unique_ptr or std::shared_ptr which you should investigate and then use and not do what you seem to be trying to do.
If you want to keep the code you have more or less, then there is one thing you need to understand, which is that your class P_array should own the pointers it contains.
To this effect, your destructor should be:
~P_array() {
unsigned int i = 0;
while(i < total)
{
std::cout << "deleting " << t_array[i] << "\n" ;
delete t_array[i]; //Ownership of pointer
i++;
}
}
Note that you don't have to test for null pointers here since delete 0; is a nop.
I notice in main.cpp you store the pointers in two different containers, but when you call ap.add(op[i]); you must think to yourself that ap now owns that pointer.
Later on you have a commented out delete op[0]; which had given you a double delete. What you should have done was something like this:
ap.t_array[0] = 0; // Null the pointer so that ap no longer owns it.
delete op[0]; // Delete pointer from the copy.
Your output to the console would then include:
deleting 0
I ran this program but I didn't get what this auto_ptr does and on which basics it shows the values?
int main(int argc,char **argv)
{
int *i= new int;
auto_ptr<int> x(i);
auto_ptr<int>y;
y=x;
count <<x.get()<<endl;
count <<y.get()<<endl;
}
This code will print a NULL address for the first auto_ptr object and some non-NULL address for the second, showing that the source object lost the reference during the assignment (=).
auto_ptr automatically releases the memory allocated by new at the end of its scope (in this case when main exits). However this example is rather stupid because the integer never receives an actual value, so I have to imagine you're seeing garbage.
This may be a bit more revealing:
int main(int argc,char **argv)
{
int *i= new int;
*i = 5;
auto_ptr<int> x(i);
auto_ptr<int>y;
y=x;
count << *(x.get()) <<endl;
count << *(y.get()) <<endl;
*x.get() = 7;
count << *(x.get()) <<endl;
count << *(y.get()) <<endl;
}
In a nutshell, auto_ptr is a templated object and when its variable goes out of scope, its destructor is called and it releases the memory acquired by it. It's a one of the simpler version of smart pointers.
auto_ptr assumes ownership over the pointer you construct it with.
ie. it will automagically destroy the object pointed to once the auto_ptr itself is destroyed.
auto_ptr<int> x(i) will make x own i
auto_ptr<int> y will make y not own anything
your y=x operation will transfer ownership of i* from x to y
so now y owns i and x owns nothing, this means that when x is destroyed nothing happens, and when y is destroyed i is deleted
For this program, auto_ptr is like a normal pointer except:
it deletes any still-pointed-too object when it goes out of scope
if you copy from one auto_ptr object to another the copied-from object will "forget" about the object it has been tracking
So, x initially takes on the value of i. Then y=x effectively asks x to forget about i (by storing a NULL sentinel value instead), and y to remember i. Then printing the pointer values returned by get will show NULL/0 for x and a non-NULL value (matching i) for y. Then as main() returns, y and x leave scope, and as y is holding the non-NULL pointer value matching i, it will delete the int object, releasing the heap memory.
auto_ptr takes over ownership of the object. The important points to note are that
It will delete the object held internally
Copying/assigning auto_ptrs transfers ownership
It will be deprecated in c++0x and replaced with unique_ptr
For these reasons it tends to be used in conjuction with RAII (Resource Acquisition is Initializtion) paradigm. For example say you allocated a variable without storing it in an auto_ptr, then you would have to manage the deletion of the memory yourself. The example below shows that this has to be done twice, once for the good path and the once for the failed path.
A* p(new A);
try {
....
}
catch (std::exception& e) {
delete p;
return;
}
delete p;
If we used auto_ptr instead it saves us having to remember to delete the allocated memory.
auto_ptr<A> p(new A);
try {
....
}
catch (std::exception& e) {
....
}