Problems with implementation of unique_ptr's move constructor - c++

I'm trying to write a unique_ptr implementation. I'm struggling with writing a move constructor. Here are my problems:
When I mark the move constructor as default, my resource is deleted twice, when I move assign a pointer (auto foo2 = std::move(foo); below) - why?
When I'm trying to assign the underlying pointer in the move constructor like this *rhs = nullptr (see implementation below), the compiler says *rhs is an rvalue and that I cannot assign anything to it.
Finally, rhs.m_ptr = nullptr works. Why does it work, when *rhs = nullptr doesn't?
My code:
#include <iostream>
namespace my
{
template <class T>
class unique_ptr
{
public:
unique_ptr()
{
m_ptr = new T;
}
unique_ptr(const unique_ptr&) = delete;
// move constructor
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
{
m_ptr = *rhs;
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
~unique_ptr()
{
delete m_ptr;
}
T* operator->()
{
return m_ptr;
}
T* operator*()
{
return m_ptr;
}
unique_ptr& operator=(const unique_ptr&) = delete;
// no move assignment yet
private:
T* m_ptr;
};
} // namespace my
struct Foo
{
Foo()
{
std::cout << "Foo" << std::endl;
}
~Foo()
{
std::cout << "~Foo" << std::endl;
}
void printHello()
{
std::cout << "Hello" << std::endl;
}
};
int main()
{
my::unique_ptr<Foo> foo;
foo->printHello();
auto foo2 = std::move(foo);
return 0;
}
On a side note, apparently I can pass a unique_ptr without any template parameter to methods inside the unique_ptr class template. Does compiler just assume it's T?
Please discard any other implementation faults that don't relate to the described problems. It's work in progress.

1) The default move constructor doesn't know about the semantics of your class. So it moves the pointer rhs, but it will not reset the other pointer, which will get deleted as well in the other destructor.
2) *rhs calls operator* and returns a temporary/rvalue T*, a copy of the internal pointer, and is not consistent with the usual operator* which should return a T& or a const T&.
3) see 2. you are returning a temporary object.
So finally, what you should have:
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
: m_ptr(rhs.m_ptr)
{
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
T& operator*() {return *m_ptr;}
const T& operator*() const {return *m_ptr;}
And so on.

You're trying too hard. You don't have to go through the external interface. Just assign values:
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
In addition, operator*() should return a T&, not a T*.

Related

Does program fail because class lack copy ctor or proper assignment operator?

I'm struggling to understand the exact reason the program fails.
Regarding the following program:
#include <iostream>
template < class T, size_t SIZE>
class Stack {
T arr[SIZE] = {};
int pos = 0;
public:
Stack & push(const T & t) {
arr[pos++] = t;
return *this;
}
Stack & push(T && t) {
arr[pos++] = std::move(t);
return *this;
}
T pop() {
return std::move(arr[--pos]);
}
};
class Foo {
std::string s;
public:
Foo(const char* s = "#") : s(s) {}
Foo(Foo && foo) : s(std::move(foo.s)) {}
Foo & operator=(Foo && foo) {
s = std::move(foo.s);
return *this;
}
void print() const { std::cout << s << std::endl; }
};
int main() {
Stack<std::string, 5> s1;
s1.push("hi").push(std::string{ "bye" });
std::cout << s1.pop() << std::endl;
Stack<Foo, 5> s3;
Foo f2;
s3.push(f2);
}
The program fails at s3.push(f2); is it because Foo doesn't have a copy ctor or is it because its assignment operator function only handles Foo&& types?
I'm suspecting it's the assignment operator function but I'm not sure it's not because of copy ctor as well.
Because you provided a custom move cosntructor and assignment operator, the compiler no longer generates default copy constructor and assignment operator.
You either need to write those too, or, even better, remove the custom move operations.
The compiler will then generate all 4 for you, and they might be better than your manually written ones (e.g. one problem is that you forgot noexcept on move operations, so standard containers will prefer making copies to moves in some scenarios).
Well, the compiler tells you:
error: use of deleted function ‘Foo& Foo::operator=(const Foo&)’
So you can solve it by adding that function:
Foo& operator=(const Foo& foo) {
s = foo.s;
return *this;
}
There are 2 solutions (I will not point out design issues assuming you are just experimenting).
As a internal data use vector<T> instead of the array of fixed size and in the push that accepts the const& move and use emplace_back (nasty and amoral, also it will leave the src in invalid state).
Provide copy constructor to Foo (this is much more easier)

How to call implicitly constructor of value wrapped in shared_ptr which is placed in std::map collection

I'm working with code generator where I can't obtain directly classname of the value which is wrapped in shared_ptr and placed in std::map.
I came to a situation where I need to create new map object but without access to classname I can't perform a valid object constructor call. I tried with the map operator at[], which calls the value constructor, but it calls shared_ptr<T> constructor and the object inside stays uninitialized.
Here the example:
#include <iostream>
#include <map>
#include <memory>
class A
{
public:
A() { std::cout << "A"; }
int getMember() const { return m_member; }
private:
int m_member = 1;
};
int main()
{
std::map<int, A> mapIntToA;
std::map<int, std::shared_ptr<A>> mapIntToAptr;
mapIntToA[1]; // runs A constructor
std::cout << mapIntToA[1].getMember();
mapIntToAptr[1]; // runs shared_ptr constructor
// cant call methods of uninitalized object
// std::cout << mapIntToAptr[1]->getMember();
// this init works, but I can't use classname 'A' in my code generator
// mapIntToAptr[1] = std::make_shared<A>();
return 0;
}
You can use the member types of std::map and std::shared_ptr to get the type of the element.
Something like
using type = typename std::map<int, std::shared_ptr<A>>::mapped_type::element_type;
mapIntToAptr[1] = std::make_shared<type>();
mapIntToAptr.emplace(1, ::std::make_shared<decltype(mapIntToAptr)::mapped_type::element_type>());
Note that use of emplace prevents a situation when map is left with nullptr value when make_shared throws.
operator[] of std::map default constructs absent value.
So, you might wrap std::shared_ptr into a class which constructs you inner class as expected, something like:
template <typename T>
struct shared_ptr_wrapper
{
std::shared_ptr<T> data = std::make_shared<T>();
operator const std::shared_ptr<T>& () const {return data;}
operator std::shared_ptr<T>& () {return data;}
const std::shared_ptr<T>& operator ->() const { return data; }
std::shared_ptr<T>& operator ->() {return data;}
const T& operator *() const { return *data; }
T& operator *() {return *data;}
};
then
std::map<int, shared_ptr_wrapper<A>> mapIntToAptr;
mapIntToAptr[1]; // runs shared_ptr constructor
std::cout << mapIntToAptr[1]->getMember(); // Ok

Overriding move-assignement operator [duplicate]

I am trying to understand the way move constructors and assignment ops work in C++11 but I'm having problems with delegating to parent classes.
The code:
class T0
{
public:
T0() { puts("ctor 0"); }
~T0() { puts("dtor 0"); }
T0(T0 const&) { puts("copy 0"); }
T0(T0&&) { puts("move 0"); }
T0& operator=(T0 const&) { puts("assign 0"); return *this; }
T0& operator=(T0&&) { puts("move assign 0"); return *this; }
};
class T : public T0
{
public:
T(): T0() { puts("ctor"); }
~T() { puts("dtor"); }
T(T const& o): T0(o) { puts("copy"); }
T(T&& o): T0(o) { puts("move"); }
T& operator=(T const& o) { puts("assign"); return static_cast<T&>(T0::operator=(o)); }
T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(o)); }
};
int main()
{
T t = std::move(T());
return 0;
}
However, when I compile and run under VS2012, the output indicates that the lvalue versions of the T0 members are called:
ctor 0
ctor
copy 0 <--
move <--
dtor
dtor 0
dtor
dtor 0
A similar situation (with a slightly different test case) happens with move assignments -- the move assignment operator of T calls the "normal" assignment operator of T0.
What am I doing wrong?
One of the more confusing things about functions taking rvalue references as parameters is that internally they treat their parameters as lvalues. This is to prevent you from moving the parameter before you mean to, but it takes some getting used to. In order to actually move the parameter, you have to call std::move (or std::forward) on it. So you need to define your move constructor as:
T(T&& o): T0(std::move(o)) { puts("move"); }
and your move assignment operator as:
T& operator=(T&& o) { puts("move assign"); return static_cast<T&>(T0::operator=(std::move(o))); }
You're only ever calling your base class's stuff with lvalues:
void foo(int&){} // A
void foo(int&&){} // B
void example(int&& x)
{
// while the caller had to use an rvalue expression to pass a value for x,
// since x now has a name in here it's an lvalue:
foo(x); // calls variant A
}
example(std::move(myinteger)); // rvalue for us, lvalue for example
That is, you need:
T(T&& o):
T0(std::move(o)) // rvalue derived converts to rvalue base
{
puts("move");
}
And:
T& operator=(T&& o)
{
puts("move assign");
T0::operator=(std::move(o)));
return *this;
}

Trying to figure out when destructors get called

Below I have a class that keeps a reference count and a class that encapsulates a pointer to another object.
When class Ptr no longer has any objects attached to it, I want to deallocate. This entails deleting the object and the reference count. It is signaled when the value of ptrcnt hits zero.
My Ptr_count class has a destructor which does its part of the deallocation. I know that this destructor will get called when the destructor for Ptr gets called, and it will free up the memory accordingly. However, I'm not so sure it gets called when the assignment operator of Ptr gets called and the code if(--refptr == 0) { delete p; } gets executed.
There is a piece of code commented out in Ptr_count. If I use this instead of the destructor, the deallocation will occur any time the value of refptr goes to zero.
My question is, is there a way for the destructor to be called during the assignment operation in Ptr or would I need to use the code in Ptr_count that is commented out in order to get proper memory deallocation?
Obviously the destructor will be called when I exit the program and the memory will be freed one way or the other but while the program is running, I think that in that instance, the reference pointer can keep decrementing even after it hits zero and that memory will still be around.
class Ptr_count {
public:
Ptr_count() : ptrcnt(new size_t(1)) { }
~Ptr_count()
{
if(ptrcnt && *ptrcnt <= 0)
delete ptrcnt;
}
size_t operator++() const
{
++(*ptrcnt);
return *ptrcnt;
}
size_t operator--() const
{
--(*ptrcnt);
/*
if(*ptrcnt == 0) {
delete ptrcnt;
return 0;
}
*/
if(ptrcnt)
return *ptrcnt;
else
return 0;
}
operator bool() const
{
return ptrcnt;
}
size_t operator*() const
{
return *ptrcnt;
}
private:
size_t* ptrcnt;
};
template <class T> class Ptr {
public:
Ptr() : p(0) {}
Ptr(T* t) : p(t) {}
Ptr(const Ptr& h) : p(h.p), refptr(h.refptr) { ++refptr; }
Ptr& operator=(const Ptr& rhs)
{
++(rhs.refptr);
if(--refptr == 0) {
delete p;
}
refptr = rhs.refptr;
p = rhs.p;
return *this;
}
~Ptr()
{
if(--refptr == 0) {
delete p;
}
}
operator bool() const { return p; }
private:
T* p;
Ptr_count refptr;
};
EDIT::
Alternatively, if class Ptr_count had it's own assignment operator, would this be a work around to the problem? If I added the below code to Ptr_count, it seems like I may be able to free the memory when the reference count reaches 0 during assignment.
void operator=(const Ptr_count& rhs)
{
if(ptrcnt == 0)
delete ptrcnt;
ptrcnt = rhs.ptrcnt;
}
First of all, if this is for self-teaching only go on. Else stop what you are doing and start using std::shared_ptr / std::unique_ptr / std::weak_ptr or if you can't use C++11 std::auto_ptr.
Now to your code:
1) It would be safer and much more natural to increment your reference count in the copy constructor Ptr_count instead of the copy constructor of Ptr, since the purpose of Ptr_count class is to manage the reference count.
You can remove Ptr's copy constructor entirely after doing so.
2) There is an unnecessary check in the assignment operator of Ptr:
// Counter *must* be greater than 0 here, else p is 0 anyways.
Ptr& Ptr::operator=(const Ptr& rhs)
{
++(rhs.refptr); // Increment your counter to 2 or above.
if(--refptr == 0) { // Decrement your counter to 1 or above.
delete p; // Never get here.
}
refptr = rhs.refptr;
p = rhs.p;
return *this;
}
3) Your biggest problem is that you are overwriting refptr and p in the assignment operator.
Ptr& operator=(Ptr const& rhs)
{
Ptr temp(rhs);
std::swap(refptr, temp.refptr);
std::swap(p, temp.p);
return *this;
}
should fix that.
4) Your decrement operator of Ptr_count is somewhat broken.
size_t Ptr_count::operator--() const
{
--(*ptrcnt); // Access address stored in ptrcnt.
if(ptrcnt) // Test if address is valid.
return *ptrcnt;
else
return 0;
}
If ptrcnt was 0 when calling this method you get an access violation because of --(*ptrcnt). Anyway, this should not be necessary, simply remove it:
size_t Ptr_count::operator--() const
{
return --(*ptrcnt);
}
tl;dr
Because code says more than 1000 words, the complete code:
class Ptr_count {
public:
Ptr_count() : ptrcnt(new size_t(1)) { }
Ptr_count(Ptr_count const& rhs) : ptrcnt(rhs.ptrcnt) { ++(*this); }
~Ptr_count()
{
if(ptrcnt && *ptrcnt <= 0)
delete ptrcnt;
}
size_t operator++()
{
return ++(*ptrcnt);
}
size_t operator--()
{
return --(*ptrcnt);
}
operator bool() const
{
return ptrcnt;
}
size_t operator*() const
{
return *ptrcnt;
}
private:
size_t* ptrcnt;
};
template <class T> class Ptr {
public:
Ptr() : p(0) {}
Ptr(T* t) : p(t) {}
Ptr& operator=(Ptr const& rhs)
{
Ptr temp(rhs);
std::swap(refptr, temp.refptr);
std::swap(p, temp.p);
return *this;
}
~Ptr()
{
if(--refptr == 0)
delete p;
}
operator bool() const { return p; }
private:
T* p;
Ptr_count refptr;
};
I think your question primarily boils down to: does this assignment call the destructor of the counter?
refptr = rhs.refptr;
The answer is no. Personally, I would be inclined to actually not wrap the counter code into a separate class and rather have it done in the Ptr class directly. Also, I think my canonical way to implement the assignment operator would take care of the correct behavior:
Ptr& Ptr::operator(Ptr other) {
this->swap(other);
return *this;
}
void Ptr::swap(Ptr& other) {
std::swap(this->p, other.p);
this->ptrcnt.swap(other.ptrcnt);
}
void Ptr_count::swap(Ptr_count& other) {
std::swap(this->ptrcnt, other.ptrcnt);
}
That said, although a simple implementation of a reference counted pointer is a fun interview question, I strong recommend to never actually implement a reference counted pointer [unless you happen to also implement the rest of the standard C++ library] and just use std::shared_ptr<T>: apart from having worked out the nitty gritty details on how to manage the count, this class implements a couple of pretty cool features which go way beyond your simple reference counted pointer and many of these features actually happen to be needed in real code.

Compile error when I declare constructor as explicit

I write a smart pointer class. When I pass it to a vector, it shows some error message when compiling. But after I delete the explict declaration, it works. What's the error?
explicit shared_ptr(const shared_ptr<T>& sp)
no matching function for call to shared_ptr<int>::shared_ptr(const shared_ptr<int> &)
#include <iostream>
#include <vector>
using namespace std;
template<class T>
class shared_ptr {
private:
struct ptr {
T* ptr;
size_t count;
void release() {
if(-- count == 0) {
delete ptr;
delete this;
}
}
};
private:
ptr* _ptr;
public:
explicit shared_ptr(T* p):
_ptr(new ptr) {
_ptr->ptr = p;
_ptr->count = 1;
}
explicit shared_ptr(const shared_ptr<T>& sp):
//explicit shared_ptr(const shared_ptr& sp):
_ptr(sp._ptr) {
++ _ptr->count;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
_ptr->release();
_ptr = sp._ptr;
++ _ptr->count;
return *this;
}
shared_ptr<T>& operator=(T* p) {
_ptr->release();
_ptr = new ptr;
_ptr->count = 1;
_ptr->ptr = p;
}
T* get() {
return _ptr->ptr;
}
T& operator*() {
return *(_ptr->ptr);
}
T* operator->() {
return _ptr->ptr;
}
~shared_ptr() {
_ptr->release();
}
};
int main() {
vector<shared_ptr<int> > vec;
vec.push_back(shared_ptr<int>(new int(10)));
}
A constructor declared as explicit can only be used when explicitly invoking it. In this line:
vec.push_back(shared_ptr<int>(new int(10)));
You are passing a value to vec which, in a C++03 implementation of the Standard Library, eventually gets copied into the vector, undergoing a copy-initialization such as:
_Tp __x_copy = __x;
The call to the copy constructor here is implicit, but your constructor is marked as explicit. Hence, the error.
Notice that the error only occurs with a C++03 compiler, or when compiling with the -std=c++03 flag, because in C++11 the call to the push_back() function with an rvalue argument (like the temporary you are instantiating) would just end up creating the value in place with an explicit call to the copy constructor.
Therefore, I assume you are working with a C++03 compiler.
Normally, explicit constructors are constructors which take one argument and are not copy-constructors, in order to avoid awkward implicit conversions (actually, in C++11, explicit makes sense also for constructors which take more than one argument, because of copy-initialization through braced initializer lists).
Copy-constructors are usually not declared as explicit.