Wrappers and Access Violation - c++

Here is my problem :
I created a generic wrapper class to handle memory management for some other classes I have where i have implemented a clone method which is supposed to clone the object by providing a new object (and new pointer to it) :
template <class T>
class Wrapper
{
public :
Wrapper() { DataPtr = 0; }
Wrapper(const T& inner)
{
if (DataPtr != 0)
DataPtr = inner.clone();
else
DataPtr = 0;
}
~Wrapper()
{
if (DataPtr != 0)
delete DataPtr;
}
Wrapper& operator=(Wrapper& original)
{
if (this != &original)
{
delete DataPtr;
DataPtr = DataPtr = 0 ? 0 : original.DataPtr->clone();
}
return *this;
}
T& operator*()
{
return *DataPtr;
}
T* operator->()
{
return DataPtr;
}
const T& operator*() const
{
return *DataPtr;
}
const T* operator->() const
{
return DataPtr;
}
private :
T* DataPtr;
};
Say I have a Class that uses in it's private attributes Wrapper < Class2 > Obj; which has a clone method :
my issue is that trying to call any member using the overloaded access operator -> gives me :
Exception thrown at 0x00007FF6122D4102 in Training.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.
And I don't understand why I don't have access since DataPtr is a member of the Wrapper Class
Both dereferencing the wrapper by doing (*Obj).SomeMethod() and using the -> operator by doing
Obj->SomeMethod() give the same results.

So the issue after all was that my operator= overload had a problem: After deleting DataPtr, I tried checking if DataPtr was null while it didn't exist. What I should do, instead, is check if the DataPtr of the object I want to assign (i.e. original) was null.
Below is the corrected code :
template <class T>
class Wrapper
{
public:
Wrapper() { DataPtr = 0; } // the default constructor which point to nothing
Wrapper(const T& inner) // constructor
{
DataPtr = inner.Clone();
}
~Wrapper()
{
delete DataPtr;
}
Wrapper(const Wrapper<T>& original) // copy constructor when pass in a Wrapper class
{
if (original.DataPtr != 0)
DataPtr = original.DataPtr->Clone();
else
DataPtr = 0;
}
// return by reference to avoid unnecessary copy
Wrapper& operator= (const Wrapper<T>& oringinal)// overloading "=" assignement operator
{
if (this != &oringinal)
{
if (DataPtr != 0)
{
delete DataPtr;
DataPtr = (oringinal.DataPtr != 0) ? oringinal.DataPtr.Clone() : 0;
}
}
return *this; // return what this point to
}
// two version of dereferencing operator *
// dereference both const and non-const objects
T& operator*() // overloading "*" to get object from pointer
{
return *DataPtr;
}
const T& operator*() const // return a const object
{
return *DataPtr;
}
// overloading "->" to access method within pointed object
const T* operator ->() const
{
return DataPtr;
}
T* operator ->()
{
return DataPtr;
}
private:
T* DataPtr;
};

Related

How do move-only iterator implement postfix ++ operator?

What is the right way to implement an iterator that iterates over a Recordset provided below in C++ style?
class Recordset
{
public:
Recordset(const Recordset&) = delete;
Recordset& operator = (const Recordset&) = delete;
Recordset(Recordset&& other) noexcept = default;
Recordset& operator = (Recordset&&) = default;
//Moves to the next record. Returns false if the end is reached.
bool Next();
//Gets the current record as an instance of type T.
template <class T>
void Get(T& val);
};
my idea is that I probably do something like this:
template <class T>
class Iterator
{
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
Iterator() = default;
Iterator(Recordset s) : m_i(std::move(s))
{
try_next();
}
Iterator(const Iterator&) = delete;
Iterator& operator = (const Iterator&) = delete;
Iterator(Iterator&& other) = default;
Iterator& operator = (Iterator&& other) = default;
T* operator-> () { return cur(); }
T* operator* () { return cur(); }
bool operator== (const Iterator& other) const noexcept
{
//They both are end().
return !m_v && !other.m_v;
}
bool operator!= (const Iterator& other) const noexcept
{
return !operator==(other);
}
Iterator& operator++ ()
{
this->try_next();
return *this;
}
Iterator operator++ (int)
{
Iterator tmp = *this; //would not compile.
this->try_next();
return tmp;
}
private:
bool try_next()
{
if (m_i.Next())
{
T val;
m_i.Get(val);
m_v = val;
return true;
}
return false;
}
T* cur()
{
T& val = *m_v;
return &val;
}
Recordset m_i;
std::optional<T> m_v;
};
template <class T>
std::ranges::subrange<Iterator<T>> make_range(Recordset& s)
{
return std::ranges::subrange(Iterator<T>(s), Iterator<T>{});
}
and use it as follows:
struct Record { int x; std::string y; };
int main()
{
Recordset s;
for (Record& r : make_range(s))
{
std::cout << r.x << r.y << std::endl;
}
return 0;
}
The frist question is how do I implement Iterator operator++ (int) if both Recordset and Iterator are move-only? (temp and this can't point to different records, because there is only one current record in the recordset). Does C++20 require it?
The second question is it a good idea to implement end() in this way? (end() is a simply an iterator containing an empty optional)
Single pass move-only input iterators (A c++20 std::input_iterator) are only required to be weakly incremental, where (void) ++i has the same effect as (void) i++. You can simply have void operator++(int) { ++*this; }. Older requirements for iterators (Cpp17InputIterator) requires iterators to be copyable, and require operator++ to return that copy.
And for your second question, you might want to use a sentinel type, something like:
template<typename T>
bool operator==(const Iterator<T>& it, std::default_sentinel_t) {
return !it.m_v;
}
// != can be rewritten from ==, so no need to write one
template <class T>
auto make_range(Recordset& s)
{
return std::ranges::subrange(Iterator<T>(s), std::default_sentinel);
}
And if you need to work with a algorithm that can't use separate sentinel types, use ranges::common_view. Your current solution also works, except you need to have this == &other || (!m_v && !other.m_v);.

Problems with implementation of unique_ptr's move constructor

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*.

How do I update this old C++ doubly linked list code to C++11?

I have the following code written in what would now be called C++98
It implements a doubly linked list, and I was considering updating it to C++11, but I had the following concerns:
it would seem that using std::unique_ptr makes sense, but there
still needs to be other pointers "sharing" the unique_ptr. I.e if
nxt is a unique_ptr, what do I do with prv and iterator::it?
it would seem that it should take an allocator as a template
paraemter, but it will be an allocator of type T, not LLNode?
are there any traits, that need to be "registered"?
if move semantics are used, what methods should be defined e.g.
operator=() && etc.?
what undefined behavior (if any) needs to be corrected?
what am i not concerned about that I should be?
An accepted answer would briefly discuss the concerns, and then reimplement in C++11 (admittedly, it's a lot of code, but most will be just cut and paste)
A good answer will briefly discuss the concerns, and then reimplement in C++11 the minimal amount to illustrate how to apply those concepts
using standard algorythms would make sense, using a standard container defeats the purpose
appropriate use of C++11 features is encouraged where it makes sense e.g. foreach as opposed to just using lambdas because it will work
This is not homework
The code:
template <class T>
class LinkedList
{
class LLNode
{
public:
LLNode() // For Sentinel (requires that T has a default constructor)
{} // set prv/nxt directly
explicit LLNode(const T& x) : data(x) // For normal nodes
{} // set prv/nxt directly
T& get_data() const
{
return const_cast<T&>(data);
}
LLNode * prv;
LLNode * nxt;
private:
T data;
};
public:
class iterator
{
public:
iterator(const LinkedList * p, LLNode * i)
: parent(const_cast<LinkedList *>(p)), it(i)
{}
iterator& operator ++() // pre
{
it = it->nxt;
return *this;
}
iterator operator ++(int) // post
{
iterator ret=*this;
it = it->nxt;
return ret;
}
iterator& operator --() // pre
{
it = it->prv;
return *this;
}
iterator operator --(int) //post
{
iterator ret=*this;
it = it->prv;
return ret;
}
T& operator *() const
{
return it->get_data();
}
T * operator ->() const
{
return &(it->get_data());
}
bool operator ==(const iterator& rhs) const
{
return it == rhs.it;
}
bool operator !=(const iterator& rhs) const
{
return it != rhs.it;
}
void erase()
{
parent->remove(it);
}
void insert_after(const T& x)
{
LLNode * add= new LLNode(x);
parent->insert(it, add);
}
void insert_before(const T& x)
{
LLNode * add= new LLNode(x);
parent->insert(it->prv, add);
}
private:
LinkedList * parent;
LLNode * it;
};
// Linked List class definition
LinkedList()
{
init();
}
LinkedList(const LinkedList& rhs)
{
init();
cp(rhs);
}
~LinkedList()
{
rm();
}
LinkedList operator =(const LinkedList& rhs)
{
if (this != &rhs)
{
rm();
cp(rhs);
}
return *this;
}
iterator begin() const
{
return iterator(this, sentinel.nxt);
}
iterator rbegin() const
{
return iterator(this, sentinel.prv);
}
iterator end() const
{
return iterator(this, const_cast<LLNode *>(&sentinel));
}
T& get_first() const // illegal if is_empty() is true
{
return sentinel.nxt->get_data();
}
T& get_last() const // illegal if is_empty() is true
{
return sentinel.prv->get_data();
}
size_t size() const
{
return count;
}
bool is_empty() const
{
return count==0;
}
void insert_first(const T& x)
{
LLNode * add= new LLNode(x);
insert(&sentinel, add);
}
void insert_last(const T& x)
{
LLNode * add= new LLNode(x);
insert(sentinel.prv, add);
}
void erase_first() // illegal if is_empty() is true
{
remove(sentinel.nxt);
}
void erase_last() // illegal if is_empty() is true
{
remove(sentinel.prv);
}
private:
void insert(LLNode * before, LLNode * added)
{
LLNode * after=before->nxt;
added->prv=before;
added->nxt=after;
before->nxt=added;
after->prv=added;
++count;
}
void remove(LLNode * node) // illegal if is_empty() is true
{
node->prv->nxt=node->nxt;
node->nxt->prv=node->prv;
delete node;
--count;
}
void cp(const LinkedList& rhs)
{
for (iterator i=rhs.begin(); i != rhs.end(); ++i)
{
insert_last(*i);
}
}
void rm()
{
LLNode * run=sentinel.nxt;
while (run != &sentinel)
{
LLNode * dead=run;
run=run->nxt;
delete dead;
}
}
void init()
{
count=0;
sentinel.nxt = sentinel.prv = &sentinel; // setup circular ref
}
LLNode sentinel;
size_t count;
};
EDIT - C++11 attempt based on Mooing Duck's answer :
template <class T, class ALLOC=std::allocator<T> >
class LinkedList
{
struct LLNode
{
LLNode * prv;
LLNode * nxt;
T& get_data() { return data; }
T data;
};
public:
class iterator
{
public:
using difference_type = ptrdiff_t;
using value_type = T;
using reference = T&;
using pointer = T*;
using iterator_category = std::bidirectional_iterator_tag;
iterator(LinkedList * p, LLNode * i) : parent(p), it(i)
{}
iterator& operator ++() // pre
{
it = it->nxt;
return *this;
}
iterator operator ++(int) // post
{
iterator ret=*this;
it = it->nxt;
return ret;
}
iterator& operator --() // pre
{
it = it->prv;
return *this;
}
iterator operator --(int) //post
{
iterator ret=*this;
it = it->prv;
return ret;
}
const T& operator *() const
{
return it->get_data();
}
T& operator *()
{
return it->get_data();
}
const T * operator ->() const
{
return &(it->get_data());
}
T * operator ->()
{
return &(it->get_data());
}
bool operator ==(const iterator& rhs) const
{
return it == rhs.it;
}
bool operator !=(const iterator& rhs) const
{
return it != rhs.it;
}
void erase()
{
parent->remove(it);
}
void insert_after(T& x)
{
auto add=parent->alloc_node(x);
parent->insert(it->nxt, add);
}
void insert_before(T& x)
{
auto add=parent->alloc_node(x);
parent->insert(it, add);
}
private:
LinkedList * parent;
LLNode * it;
};
class const_iterator
{
public:
using difference_type = ptrdiff_t;
using value_type = const T;
using reference = const T&;
using pointer = const T*;
using iterator_category = std::bidirectional_iterator_tag;
const_iterator(const LinkedList * p, const LLNode * i) : parent(p),
it(const_cast<LLNode *>(i))
{}
const_iterator(iterator& cvt) : parent(cvt.parent), it(cvt.it)
{}
const_iterator& operator ++() // pre
{
it = it->nxt;
return *this;
}
const_iterator operator ++(int) // post
{
const_iterator ret=*this;
it = it->nxt;
return ret;
}
const_iterator& operator --() // pre
{
it = it->prv;
return *this;
}
const_iterator operator --(int) //post
{
const_iterator ret=*this;
it = it->prv;
return ret;
}
const T& operator *() const
{
return it->get_data();
}
const T * operator ->() const
{
return &(it->get_data());
}
bool operator ==(const const_iterator& rhs) const
{
return it == rhs.it;
}
bool operator !=(const const_iterator& rhs) const
{
return it != rhs.it;
}
private:
const LinkedList * parent;
LLNode * it;
};
using my_alloc=typename
std::allocator_traits<ALLOC>::template rebind_alloc<LLNode>;
using my_traits=typename
std::allocator_traits<ALLOC>::template rebind_traits<LLNode>;
// Linked List class definition
LinkedList(const ALLOC& alloc = ALLOC() ) : mem(alloc)
{
init();
}
LinkedList(const LinkedList& rhs) : mem(rhs.mem)
{
init();
cp(rhs);
}
LinkedList(LinkedList&& rhs) : mem(rhs.mem) // Move
{
init();
shallow_cp(rhs);
}
~LinkedList()
{
rm();
}
LinkedList operator =(const LinkedList& rhs)
{
if (this != &rhs)
{
rm();
cp(rhs);
}
return *this;
}
LinkedList operator =(LinkedList&& rhs) // Move
{
if (this != &rhs)
{
rm();
shallow_cp(rhs);
}
return *this;
}
const_iterator begin() const
{
return const_iterator(this, sentinel.nxt);
}
iterator begin()
{
return iterator(this, sentinel.nxt);
}
const_iterator rbegin() const
{
return const_iterator(this, sentinel.prv);
}
iterator rbegin()
{
return iterator(this, sentinel.prv);
}
const_iterator end() const
{
return const_iterator(this, &sentinel);
}
iterator end()
{
return iterator(this, &sentinel);
}
T& front() // illegal if is_empty() is true
{
return sentinel.nxt->get_data();
}
T& back() // illegal if is_empty() is true
{
return sentinel.prv->get_data();
}
size_t size() const
{
return count;
}
bool is_empty() const
{
return count==0;
}
void insert_first(const T& x)
{
LLNode * add=alloc_node(x);
insert(&sentinel->nxt, add);
}
void insert_last(const T& x)
{
LLNode * add=alloc_node(x);
insert(&sentinel, add);
}
void erase_first() // illegal if is_empty() is true
{
remove(sentinel.nxt);
}
void erase_last() // illegal if is_empty() is true
{
remove(sentinel.prv);
}
private:
LLNode * alloc_node(const T& x)
{
auto ret = my_traits::allocate(mem,1);
my_traits::construct(mem, &(ret->data), x);
return ret;
}
void unalloc_node(LLNode * dead)
{
my_traits::deallocate(mem, dead, 1);
}
void insert(LLNode * after, LLNode * added)
{
LLNode * before=after->prv;
added->prv=before;
added->nxt=after;
before->nxt=added;
after->prv=added;
++count;
}
void remove(LLNode * node) // illegal if is_empty() is true
{
node->prv->nxt=node->nxt;
node->nxt->prv=node->prv;
unalloc_node(node);
--count;
}
void cp(const LinkedList& rhs)
{
mem = rhs.mem;
for (const_iterator i=rhs.begin(); i != rhs.end(); ++i)
{
insert_last(*i);
}
}
void shallow_cp(LinkedList& rhs)
{
if (rhs.count)
{
count=rhs.count;
sentinel=rhs.sentinel; // shallow copy
// fix the links to the old sentinel
sentinel.nxt.prv=&sentinel;
sentinel.prv.nxt=&sentinel;
rhs.init();
}
}
void rm()
{
LLNode * run=sentinel.nxt;
while (run != &sentinel)
{
LLNode * dead=run;
run=run->nxt;
unalloc_node(dead);
}
}
void init()
{
count=0;
sentinel.nxt = sentinel.prv = &sentinel; // setup circular ref
}
LLNode sentinel;
size_t count;
my_alloc mem;
};
Is anything missing/wrong?
it would seem that using std::unique_ptr makes sense, but there still needs to be other pointers "sharing" the unique_ptr. I.e if nxt is a unique_ptr, what do I do with prv and iterator::it?
Don't. (1) It makes various internal algorithms trickier to perform without accidentally deleting a node, and (2) unique_ptr stores the deleter, in your case that's either (A) a copy of the iterator, or (B) a pointer to the iterator. Either one is a waste of space. The container should store the allocator, and the container should handle deletions.
it would seem that it should take an allocator as a template paraemter, but it will be an allocator of type T, not LLNode?
Containers take an allocator of type T, though all of them use a rebound type internally. The standard is that allocators to containers take type T, and that way every std::vector<T, allocator<?>> matches every other. Also, outsiders should not be able to access LLNode. You'd probably store a given_allocator<LLNode> internally though. That's why we have rebinding.
are there any traits, that need to be "registered"?
For containers, no. There are interfaces to match, but those are relatively obvious.
Yes, your iterators are supposed to register traits, but that's easily done by merely adding five typedefs at the front.
typedef ptrdiff_t difference_type; //usually ptrdif_t
typedef T value_type; //usually T
typedef T& reference; //usually T&
typedef T* pointer; //usually T*
typedef std::bidirectional_iterator_tag iterator_category;
if move semantics are used, what methods should be defined e.g. operator=() && etc.?
Obviously the container should be move constructable and move assignable if it makes sense, which it does 99.99% of the time. Even std::array has move operators. Also decide which member functions should support move-only-T (insert range via iterators, but not insert range + count), and which member functions support any T (emplace one).
what undefined behavior (if any) needs to be corrected?
It would appear that your insert(iterator, data) inserts the data after the iterator, when the standard is to insert before the iterator. Your way makes it impossible to add data to the beginning, and makes it possible to add data after the after-the-end.
Your iterators have but do not need remove and insert functions. I wouldn't have mentioned it, but in this case they require the iterators to be twice as big as needed. I'd offer a tentative suggestion to remove them, but only tentative. That's a small penalty, and potentially a useful feature.
Your operator = deallocates everything and then reallocates. It might be handy to simply copy element by element skipping that when possible. Trickier to code though.
You're missing a constructor that constructs from a pair of iterators.
Your iterators allow mutating of data from const containers. THIS ONE IS BIG
get_first and get_last are normally called front and back.
what am i not concerned about that I should be?
The big one that everyone overlooks is exception safety. Your code is exception safe, but that appears to be because your code is amazingly simple, and you skipped all of the "hard" parts :P
For more information review these:
Writing your own STL Container
How to implement an STL-style iterator and avoid common pitfalls?
Edit for your C++11 followup:
missing:
template LLNode(U&& x) //or, one T&& and one const T&
LinkedList(std::initializer_list x)
T& LLNode::get_data() const //important, your code doesn't even compile
T* iterator::operator->() const //important
const_iterator cbegin() const
const_iterator cend() const
const T& front() const //important
const T& back() const //important
template void assign(iter, iter);
void assign(std::initializer_list);
void swap(const X&);
Incorrect:
insert_* should probably take a U&& and use std::forward<U>(x). Variadic templates would be better.
no iterator pair constructor.
memory leak in alloc_node, and you should be constructing a LLNode, you have uninitialized prv/nxt.
unalloc_node never destroys, only deallocates, leading to a memory leak
Missing but pretty unimportant
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
reverse_iterator rbegin(); //optional
const_reverse_iterator rbegin() const;
const_reverse_iterator crbegin() const;
reverse_iterator rend(); //optional
const_reverse_iterator rend() const;
const_reverse_iterator crend() const;
Optional:
not sure why my_alloc and my_traits and they're identical.
inherit privately from the allocator. It's a little wierd, but commonly saves you 8 bytes.

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.

avoiding dangling reference in array subscription operator

How to avoid dangling reference in array subscription operator in some vector implementation below? If realloc changes the pointer then references previously obtained from operator[] are no longer valid. I cannot use new/delete for this. I have to use malloc/realloc/free.
template <class T>
class Vector
{
public:
typedef size_t size_type_;
...
T& operator[] (const size_type_);
void push_back (const T&);
...
private:
const size_type_ page_size_;
size_type_ size_;
size_type_ capacity_;
T* buffer_;
};
template<class T>
inline
T&
some_namespace::Vector<T>::operator[] (const size_type_ index)
{
ASSERT(index < size_);
return buffer_[index];
}
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
}
buffer_[size_++] = val;
}
By the way, the source of dangling reference in the code was this:
v_.push_back(v_[id]);
where v_ is an instance of Vector. To guard against this the new push_back is:
template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
if (size_ >= capacity_)
{
const T val_temp = val; // copy val since it may come from the buffer_
capacity_ += page_size_;
T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));
if (temp != NULL)
{
buffer_ = temp;
}
else
{
free(buffer_);
throw some_namespace::UnexpectedEvent();
}
buffer_[size_++] = val_temp;
}
else
{
buffer_[size_++] = val;
}
}
There are basically three things you can do:
Accept it as is and document it. This is what the STL does and it's actually quite reasonable.
Don't return a reference, return a copy. Of course, this means you can't assign to the vector's element anymore (v[42] = "Hello"). In that case you want a single operator[] which is marked const.
Return a proxy object which stores a reference to the vector itself and the index. You add an assignment to the proxy from const T& to allow writing to the vector's element and you need to provide some way to access/read the value, most likely an implicit operator const T&. Since the proxy object asks the vector on each access for the current position of the element, this works even if the vector changed the location of those elements between calls. Just be careful with multi-threading.
Here's a sketch for the proxy, untested and incomplete:
template<typename T> class Vector
{
private:
// ...
// internal access to the elements
T& ref( const size_type_ i );
const T& ref( const size_type_ i ) const;
class Proxy
{
private:
// store a reference to a vector and the index
Vector& v_;
size_type_ i_;
// only the vector (our friend) is allowed to create a Proxy
Proxy( Vector& v, size_type_ i ) : v_(v), i_(i) {}
friend class Vector;
public:
// the user that receives a Proxy can write/assign values to it...
Proxy& operator=( const T& v ) { v_.ref(i_) = v; return *this; }
// ...or the Proxy can convert itself in a value for reading
operator const T&() const { return v_.ref(i_); }
};
// the Proxy needs access to the above internal element accessors
friend class Proxy;
public:
// and now we return a Proxy for v[i]
Proxy operator[]( const size_type_ i )
{
return Proxy( *this, i );
}
};
Note that the above is incomplete and the whole technique has some drawbacks. The most significant problem is that the Proxy "leaks" in the API and therefore some use cases are not met. You need to adapt the technique to your environment and see if it fits.