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.
Related
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;
};
I'm asked to write a SmartPointer class. One of the constructors takes a pointer variable, and I assume that I should simply copy the pointer to the relevant variable. But when I try, I get a segmentation error. Here is the content of the header file and my implementation of Pointer Constructor.
class ReferenceCount {
public:
size_t AddRef() {
return ++count;
}
size_t Release() {
return --count;
}
size_t getCount() const {
return count;
}
private:
size_t count = 0; // Reference count
};
template<typename T>
class SmartPointer {
private:
void free();
// pointer to actual data
T *dataPointer;
// Reference count
ReferenceCount *referenceCount;
public:
//Constructor
SmartPointer();
// Copy constructor
SmartPointer(const SmartPointer<T> &sp);
explicit SmartPointer(T *pValue);
// Assignment operator
SmartPointer<T> &operator=(const SmartPointer<T> &sp);
SmartPointer<T> &operator=(T *pValue);
// Destructor
~SmartPointer();
T &operator*() const;
T *operator->() const;
T *get() const;
ReferenceCount *getReferenceCount() const;
};
The constructor:
template<typename T>
SmartPointer<T>::SmartPointer(T *pValue) {
dataPointer = pValue;
referenceCount = nullptr;
}
You appear to be writing something similar to shared_ptr<T>.
For a shared_ptr-like smart pointer, each smart pointer has both a pointer-to-object and a pointer-to-control-block.
When you are constructed with a pointer-to-object, you are responsible to create the control block.
In your case, your control block name is ReferenceCount.
So add a new ReferenceCount to that constructor. Probably start it off with a count of 1.
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*.
I must write operator overloading += but I don't know how to do it correctly (I began to write it a the end of code it wasn't correct so i delete all except you see).
#include <iostream>
using namespace std;
class dek
{
private:
int *x,na4,kon,n,razmer;
public:
dek(int m)
{
x=new int[m];
n=m;
na4=1;
kon=0;
razmer=0;
}
~dek()
{
delete [] x;
}
void v_n(int a)
{
if(razmer!=n)
{
na4--;
if(na4<0)na4=n-1;
x[na4]=a;
razmer++;
}
else cout<<"dek polon\n";
}
void v_k(int b)
{
if(razmer!=n)
{
kon++;
if(kon>n-1)kon=0;
x[kon]=b;
razmer++;
}
else cout<<"dek polon\n";
}
int size()
{
return razmer;
}
void u_n()
{
if(razmer!=0)
{
na4++;
if(na4>n-1)na4=0;
razmer--;
}
else cout<<"dek pust\n";
}
void u_k()
{
if(razmer!=0)
{
kon--;
if(kon<0)kon=n-1;
razmer--;
}
else cout<<"dek pust\n";
}
void pe4at()
{
int i=na4;
if(razmer!=0)
{
while(1)
{
cout << x[i] << " ";
if(i==kon)break;
i++;
if(i>n-1)i=0;
}
cout << "\n";
}
}
dek& operator = (dek const& b)
{
if(&b!=this)
{
delete [] x;
x=new int[b.n];
n=b.n;
razmer=b.razmer;
na4=b.na4;
kon=b.kon;
if(razmer!=0)
{
int i=na4;
while(1)
{
x[i]=b.x[i];
if(i==kon)break;
i++;
if(i>n-1)i=0;
}
}
}
return *this;
}
dek const operator +(dek const& b)const
{
dek s(n+b.n);
s.n=n+b.n;
s.razmer=razmer+b.razmer;
s.na4=0;
s.kon=s.razmer-1;
if(razmer!=0)
{
int j=0,i=na4;
while(1)
{
s.x[j]=x[i];
if(i==kon)break;
i++;
if(i>n-1)i=0;
j++;
if(j>s.n-1)j=0;
}
}
if(b.razmer!=0)
{
int j=razmer,i=b.na4;
while(1)
{
s.x[j]=b.x[i];
if(i==b.kon)break;
i++;
if(i>b.n-1)i=0;
j++;
if(j>s.n-1)j=0;
}
}
return s;
}
dek operator +=(dek const& b)
{
}
};
Well, the results of a += b; should be equivalent to a = a + b;;
since you have already defined an operator+, you know what these
semantics are. Once common practice is to define operator+= first,
and then implement operator+ (usually as a free function) in terms of
+=:
MyClass
operator+( MyClass const& lhs, MyClass const& rhs )
{
MyClass results( lhs );
results += rhs;
return results;
}
You then define operator+= to operate directly on the class members:
MyClass&
MyClass::operator+=( MyClass const& other )
{
n += other.n;
razmer += other.razmer;
// ...
return *this;
}
(Although there are good reasons for making it a non-member,
traditionally, operator+= is a member. Probably because operator=
is required to be a member.)
Also, traditionally, operator+= returns a reference, because this
most resembles the behavior of the operator on built-in types.
Finally, on a completely different issue: you're missing a copy
constructor (which in your case means a double deletion if you do copy),
and your operator= is broken (think of what will happen if the x =
new int[b.n]; fails and throws an std::bad_alloc). The classical
solution for this would be to implement deep copy in the copy
constructor (using more or less the same logic you use in setting the
variables in your assignment operator), and in the assignment operator,
to construct a copy, then swap the elements. This isn't strictly
necessary, but whatever you do, you must do the new (and anything else
which may fail) before changing the values in the object being assign.
(If you do this, the test for self assignment is useless; the need for a
test for assignment is usually a signal that the assignment operator is
broken.)
you need to implement void add(const dek &other) - split it out of "+" operator.
you can then implement + as
dek new_dek(*this);
new_dek.add(other);
return new_dek;
And you can implement += as
this->add(other);
return *this;
(assuming you change the return type of += to dek &)
i try to use the std::map with
class DEMO {
public:
DEMO();
virtual ~DEMO();
DEMO &operator =(const DEMO &d);
DEMO(const DEMO& d);
BYTE* Arr() const;
private:
BYTE *m_array;
};
DEMO &DEMO::operator =(const DEMO &d) {
memcpy(m_array, d.Arr(), 1);
return *this;
}
DEMO::DEMO(const DEMO& d) {
//call operator=
*this = d;
}
const BYTE* DEMO::Arr() const {
return m_array;
}
DEMO::DEMO() {
m_array = new BYTE[1];
}
DEMO::~DEMO() {
if (m_array != 0)
delete [] m_array;
}
class MyClass {
private:
typedef map<unsigned int,DEMO> t_mapType;
t_mapType m_map;
void Test();
}
void MyClass::Test() {
DEMO myDEMO;
m_map[1] = myDEMO;
}
if i call Test() within the class, i get the error assert _CrtIsValidHeapPointer...
i checked with breakpoints and i see, during assingment (m_map[1] = myDEMO;) the destructor of DEMO gets called and i get the error on delete [] m_array; - how i make this running ?
The copy constructor and assignment operators are wrong. You need to allocate memory for m_array before doing memcpy. When you try to insert a DEMO object into the map using m_map[1] = myDEMO; a copy of the myDEMO is being created. To create a copy the DEMO class's copy ctor is used. Since the memory for m_array is not allocated while doing this copy, the memcpy call is crashing.
It's almost always wrong to base the copy ctor implementation on the assignment operator's. An assignment operator has to tear down the object's old state and build a new state. The copy ctor only has to build a new state.
If you want to do it this way, your copy ctor first needs to build a default state for the assignment operator to tear down. Of course, that's wasting resources.
That said, actually you should use the copy-and-swap idiom to base your assignment operator on your dtor and copy ctor instead. See here for a more thorough explanation of the above.
Use a std::vector to manage your BYTE*.
class DEMO {
public:
DEMO();
virtual ~DEMO();
//DEMO &operator =(const DEMO &d);
//DEMO(const DEMO& d);
const std::vector<BYTE>& Arr() const;
private:
std::vector<BYTE> m_array;
};
// Doesn't even need to be implemented by us anymore
//DEMO &DEMO::operator =(const DEMO &d) {
// memcpy(m_array, d.Arr(), 1);
// return *this;
//}
// Again, doesn't need to be implemented
//DEMO::DEMO(const DEMO& d) {
// //call operator=
// *this = d;
//}
// Just changed to return const vector&.
const std::vector<BYTE>& DEMO::Arr() const {
return m_array;
}
// Changed to use initializer list
DEMO::DEMO()
: m_array(1) {
// Old code: m_array = new BYTE[1];
}
// Doesn't need any code by us
DEMO::~DEMO() {
//if (m_array != 0)
// delete [] m_array;
}
i changed my code to use pointer to object instead of object for the value part in the map:
typedef map<unsigned int,DEMO*> t_mapType;
so i can assing as follow:
m_map[1] = new DEMO();
and i have to use a own free-mem method:
void DEMO::Clear() {
if (m_map.size() > 0) {
for (t_mapType::const_iterator it = m_map.begin(); it != m_mp.end(); ++it) {
if (it->second != 0)
delete it->second;
}
m_map.clear();
}
}