Is there a way to do down cast of std::shared_ptr without incurring creation of a new shared_ptr and a move as its done in std::dynamic_pointer_cast?
Also there can be a scenario I am not intending to own the shared_ptr , in those case I would like to create a reference to existing shared_ptr
e.g.
const std::shared_ptr<derived>& d = std::dynamic_pointer_cast<derived>(b); //
where b is std::shared_ptr<base>
Here I am getting a reference of b , but still paying for a temp shared_ptr.
Is there a better way apart from using raw pointers here?
You can get the pointer or reference from a shared_ptr and downcast it:
std::shared_ptr<Base> b(new Derived);
auto& d1 = dynamic_cast<Derived&>(*b);
auto* d2 = dynamic_cast<Derived*>(&*b);
auto* d3 = dynamic_cast<Derived*>(b.get());
Note that destroying the original shared_ptr may invalidate the raw pointers and references.
Related
For example:
#include <memory>
shared_ptr<map<int, int>> ptr1 = make_shared<map<int, int>>();
map<int, int>::const_iterator iter = ptr1->begin();
shared_ptr<map<int, int>> ptr2 = make_shared<map<int, int>>();
ptr1 = ptr2;
My question: Could the iter add the counts of ptr1? In this case, will the ptr1's memory address free after ptr1 = ptr2?
No, it cannot.
When ptr1=ptr2 runs, the last shared pointer to the first map is gone and the object is destroyed.
iter is now invalid, and almost any use besides destruction or assigning over it results in undefined behavior.
Creating an iterator that makes its container persist is possible, if quite intrusive. As a general rule, you should instead learn to keep object lifetime simple and work with object lifetime, not expect smart pointers to solve it for you.
Consider the following class:
class Vector{
int dim; //dimension of array v
Complex* v; //Complex is another class
public:
Vector(int dim = 0):dim(dim){(dim)?(v=new Complex[dim]):(v=nullptr);}
Vector(int dim, const Complex* c):dim(dim),v(new Complex[dim]){
for(int i=0;i<dim;i++) v[i]=c[i];}
Vector(const Vector& a):dim(a.dim),v(new Complex[a.dim]){
for(int i=0;i<dim;i++) v[i]=a.v[i];}
~Vector(){if(dim)delete [] v,v=nullptr;}
friend Vector& operator >> (Vector& is,Complex& z){
Vector copie(is);
is.~Vector();
is.Vector::Vector(is.dim+1);}
};
I try to overload the >> operator in order to add elements to v.
My idea was to create a copy, then call dctor and the ctor for the object to
be modified via >> operator.
I'm stuck after getting this error:
In function ‘Vector& operator>>(Vector&, Complex&)’:
main.cc:56:20: error: cannot call constructor ‘Vector::Vector’ directly
is.Vector::Vector(is.dim+1);
I'm not allowed to use containers!!
Please help me!
That's right, you can't call the constructor directly. Probably you want to use placement new.
friend Vector& operator >> (Vector& is,Complex& z){
Vector copie(is);
is.~Vector();
// is.Vector::Vector(is.dim+1);
new(&is) Vector(is.dim + 1);
return is;
}
Even then the code may not be semantically correct.
Having said that, this is not the recommended way to do it for
the last 20 years. Watch this Jon Kalb "Exception-Safe Code, Part
I" for an explanation (the example is almost the same). The
recommended way is to implement this in terms of other operations like
copy or swap.
Minor syntactic detail, operator>> is confusing, use operator<< at worst.
There is no need for calling the destructor and calling the constructor. Steps you can take to make your function work:
Allocate memory to hold the current objects plus the additional object.
Copy the objects from the old memory location to the new memory location.
Delete the old memory.
Associate the newly allocated memory with the input object.
friend Vector& operator>>(Vector& is, Complex& z){
// Allocate memory
Complex* vnew = new Complex[dim+1];
// Copy objects to new memory.
std::copy(is.v, is.v + is.dim, vnew);
vnew[is.dim] = z;
// Delete the old memory.
delete [] is.v;
// Use the new memory
is.v = vnew;
// Increment dim.
is.dim++;
return is;
}
Having said that, I think you are using the wrong function to insert an element to Vector. operator>> is for extracting data from. operator<< is for inserting data to. You should use operator<< to insert an element to a Vector.
friend Vector& operator<<(Vector& is, Complex const& z){
...
}
I can't understand the function move in c++11.
From here, I got things below:
Although note that -in the standard library- moving implies that the
moved-from object is left in a valid but unspecified state. Which
means that, after such an operation, the value of the moved-from
object should only be destroyed or assigned a new value; accessing it
otherwise yields an unspecified value.
In my opinion, after move(), the moved-from object has been "clear". However, I've done a test below:
std::string str = "abcd";
std::move(str);
std::cout<<str;
I got abcd on my screen.
So has the str been destroyed? If so, I could get abcd because I'm just lucky? Or I misunderstood the function move?
Besides, when I read C++ Primer, I got such a code:
class Base{/* ... */};
class D: public Base{
public:
D(D&& d): Base(std::move(d)){/* use d to initialize the members of D */}
};
I'm confused now. If the function move will clear the object, the parameter d will be clear, how could we "use d to initialize the members of D"?
std::move doesn't actually do anything. It's roughly analogous to a cast expression, in that the return value is the original object, but treated differently.
More precisely, std::move returns the object in a form which is amenable to its resources being 'stolen' for some other purpose. The original object remains valid, more or less (you're only supposed to do certain special things to it, though that's primarily a matter of convention and not necessarily applicable to non-standard-library objects), but the stolen-away resources no longer belong to it, and generally won't be referenced by it any more.
But! std::move doesn't, itself, do the stealing. It just sets things up for stealing to be allowed. Since you're not doing anything with the result, let alone something which could take advantage of the opportunity, nothing gets stolen.
std::move doesn’t move anything. std::move is merely a function template that perform casts. std::move unconditionally casts its argument to an rvalue,
std::move(str);
With this expression you are just doing type cast from lvalue to rvalue.
small modification in program to understand better.
std::string str = "abcd";
std::string str1 = std::move(str);
std::cout<<str<<std::endl;
std::cout<<str1<<std::endl;
str lvalue typecast to rvalue by std::move, std::string = std::move(str); =>this expression call the string move constructor where actual stealing of resources take placed. str resources(abcd) are steeled and printed empty string.
Here is sample implementation of move function. Please note that it is not complete implementation of standard library.
template<typename T> // C++14; still in
decltype(auto) move(T&& param) // namespace std
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(param);
}
Applying std::move to an object tells the compiler that the object is eligible to be moved from. It cast to the rvalue.
class Base{/* ... */};
class D: public Base{
public:
D(D&& d): Base(std::move(d)){/* use d to initialize the members of D */}
};
Base(std::move(d)) it will do up-casting only move the base class part only.
Here one more interesting thing to learn for you. If you do not invoke base class destructor with std::move like D(D&& d): Base(d) then d will be considered as lvalue and copy constructor of Base class involved instead of move constructor. Refer for more detail Move constructor on derived object
Why can not I use new [ ] with smart_pointers?
Actually I can not understand this piece of text.
Caution You should use an auto_prt or shared_ptr object only for
memory allocated by new, not for memory allocated by new []. You
should not use auto_ptr, shared_ptr,orunique_ptr for memory not
allocated via new or, in the case of unique_ptr, via new or new[].
Why can not I use new[] with smart pointers?
In general you can, but that smart pointer must be aware of the fact that it stores a dynamically allocated array, not a single object. This is because objects allocated with operator new[] should be deallocated with operator delete[], not delete. How can a smart pointer know which operator should be applied?
The distinction is made by providing a specialization of smart pointer class templates for array types, like it is currently done in std::unique_ptr<T>:
std::unique_ptr<int> ptr(new int); // will call delete
std::unique_ptr<int[]> arr(new int[5]); // will call delete[]
↑↑
DEMO
However, that syntax does not (yet) apply to all smart pointer types available in the Standard Library.
For comparison, the Boost Smart Pointers library provides separate class templates for storing pointers to dynamically allocated arrays:
boost::shared_array<int> arr1(new int[5]); // will call delete[]
// ~~~~^
boost::scoped_array<int> arr2(new int[5]); // will call delete[]
// ~~~~^
DEMO 2
You should use an auto_ptr or shared_ptr object only for memory allocated by new, not for memory allocated by new [].
std::auto_ptr<T>(† 2017)1 applies a plain delete operator to a pointer it stores, and there is no way to change that behavior. As such, storing a pointer to an array is not an option.
As far as std::shared_ptr<T> is concerned, by default it does the same (calls operator delete). To change that behavior, and properly deallocate a memory area of a stored array, you could use a custom deleter, like std::default_delete<T[]>:
std::shared_ptr<int> arr(new int[5], std::default_delete<int[]>{});
↑↑
or some other provided by yourself:
std::shared_ptr<int> arr(new int[5], [](int* ptr) { delete[] ptr; } );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
DEMO 3
However, a missing specialization for std::shared_ptr<T[]> implies no operator[] that could let you easily access the elements of a stored array, which leads to unintuitive syntax like arr.get()[0].
With proposal N4077 introduced, there will be a specialization for array type pointers:
std::shared_ptr<int[]> arr(new int[5]); // will call delete[]
↑↑
You should not use auto_ptr, shared_ptr, or unique_ptr for memory not allocated via new or, in the case of unique_ptr, via new or new[].
This excerpt simply states that one should not construct a smart pointer from a pointer to an object that was not allocated dynamically, as (by default) it would result in calling delete on something that was not allocated with new (ditto new[]/delete[]).
What is the difference between unique_ptr<double[]> p1(new double[2]);, unique_ptr<double> p2(new double[2]);, unique_ptr<double[]> p3(new double(2)); ?
std::unique_ptr<double[]> p1(new double[2]);
OK: Constructs a unique_ptr from (and takes ownership of) a pointer to an array of two doubles. It will call delete[] to deallocate the memory pointed.
std::unique_ptr<double> p2(new double[2]);
Wrong: Constructs a unique_ptr from (and takes ownership of) a pointer to an array of two doubles. It will call delete (!) to deallocate the memory pointed. (possibly undefined behavior - a mismatch between new[] and delete).
std::unique_ptr<double[]> p3(new double(2));
Wrong: Constructs a unique_ptr from (and takes ownership of) a pointer to a single double initialized to value 2. It will call delete[] (!) to deallocate the memory pointed. (possibly undefined behavior - a mismatch between new and delete[]).
1 std::auto_ptr<T> is deemed deprecated in favor of std::unique_ptr<T> since C++11, and will be removed from the Standard Library in C++1z according to N4168.
Examples:
#include <memory>
int
main()
{
auto p1 = std::unique_ptr<char[]>{new char[3]}; // C++11
auto p2 = std::shared_ptr<char>{new char[3], [](char* p) {delete [] p;}}; // C++11
auto p3 = std::make_unique<char[]>(3); // C++14
}
The first and second are good for C++11 and forward. The 3rd was introduced in C++14. The first and third represent unique ownership of the new, and the second has shared ownership of the new.
I am trying to use a FILE pointer multiple times through out my application
for this I though I create a function and pass the pointer through that. Basically I have this bit of code
FILE* fp;
_wfopen_s (&fp, L"ftest.txt", L"r");
_setmode (_fileno(fp), _O_U8TEXT);
wifstream file(fp);
which is repeated and now instead I want to have something like this:
wifstream file(SetFilePointer(L"ftest.txt",L"r"));
....
wofstream output(SetFilePointer(L"flist.txt",L"w"));
and for the function :
FILE* SetFilePointer(const wchar_t* filePath, const wchar_t * openMode)
{
shared_ptr<FILE> fp = make_shared<FILE>();
_wfopen_s (fp.get(), L"ftest.txt", L"r");
_setmode (_fileno(fp.get()), _O_U8TEXT);
return fp.get();
}
this doesn't simply work. I tried using &*fp instead of fp.get() but still no luck.
You aren't supposed to create FILE instances with new and destroy them with delete, like make_shared does. Instead, FILEs are created with fopen (or in this case, _wfopen_s) and destroyed with fclose. These functions do the allocating and deallocating internally using some unspecified means.
Note that _wfopen_s does not take a pointer but a pointer to pointer - it changes the pointer you gave it to point to the new FILE object it allocates. You cannot get the address of the pointer contained in shared_ptr to form a pointer-to-pointer to it, and this is a very good thing - it would horribly break the ownership semantics of shared_ptr and lead to memory leaks or worse.
However, you can use shared_ptr to manage arbitrary "handle"-like types, as it can take a custom deleter object or function:
FILE* tmp;
shared_ptr<FILE> fp;
if(_wfopen_s(&tmp, L"ftest.txt", L"r") == 0) {
// Note that we use the shared_ptr constructor, not make_shared
fp = shared_ptr<FILE>(tmp, std::fclose);
} else {
// Remember to handle errors somehow!
}
Please do take a look at the link #KerrekSB gave, it covers this same idea with more detail.