In the code below is implemented a smart pointer that executes fine, but at the end I get the following message:
Here is the code:
smart_ptr.h:
#ifndef SMART_PTR_H
#define SMART_PTR_H
/*
Class: Auto_ptr
It implements a generic
smart pointer that doesn't need
to be deleted explicitly, i.e.
it provides garbage collection.
*/
template<class T>
class Auto_ptr{
public:
// constructors
explicit Auto_ptr(T* p = nullptr): value(p) { }; // constructor
Auto_ptr(Auto_ptr& p); // copy constructor
Auto_ptr& operator= (const Auto_ptr& p); // copy assignment
~Auto_ptr() { std::cout << "pointer deleted\n"; delete value; } // destructor
// access operators
const T& operator* () const { return *value; } // dereference operator
const T* operator->() const { return value; } // indirect class member access (arrow) operator
// non-modifying members
T* get() { return value; } // getter method
void reset(T* v); // reassing new value(default value: nullptr)
T* release(); // transfers the object to another pointer; without destroying it
private:
// data member
T* value;
};
//--------------------------------------------------------------------------------------------------------
// class Auto_ptr member implementations
// Constructors
// copy constructor
template<class T>
Auto_ptr<T>::Auto_ptr(Auto_ptr& p) {
value = p.release();
}
// copy assignment
template<class T>
Auto_ptr<T>& Auto_ptr<T>::operator= (const Auto_ptr& p ) {
if (this == &p) return *this;
if (value) delete value;
value = p.value;
return *this;
}
/*
Function: release()
Use: T ptr = auto_ptr_obj.release();
It transfers the pointer value to the
caller, setting the data member value
to nullptr.
*/
template <class T>
T* Auto_ptr<T>::release() {
T* temp = value;
value = nullptr;
return temp;
}
/*
Function: reset()
Use: auto_ptr_obj.release(new_pointer);
It deletes the object pointer to by
pointer value and assings new_pointer;
*/
template <class T>
void Auto_ptr<T>::reset(T* v) {
delete value;
value = v;
}
#endif
main.cpp:
#include <iostream>
#include "smart_ptr.h"
#include "assert.h"
//=====================================================================
void test1 () {
std::cout <<"\nTest constructor and get() member.\n";
Auto_ptr<int> p(new int);
*p.get() = 5;
std::cout <<"p points to: "<< *p << "\n";
//assert(*p, 5);
std::cout <<"TEST 1 DONE\n";
}
void test2 () {
std::cout <<"\nTest reset() and release() members.\n";
Auto_ptr<int> p(new int);
*p.get() = 5;
std::cout <<"p points to: "<< *p << "\n";
p.reset(new int(10));
std::cout <<"reset() p points to: "<< *p << "\n";
//assert(*p, 10);
int *temp = p.release();
std::cout <<"caller of release(), temp points to: "<< *temp << "\n";
//assert(*temp, 10);
// nullptr dereferece error
// std::cout <<"p after being release()d points to: "<< *p << "\n";
std::cout <<"TEST 2 DONE\n";
}
void test3 () {
std::cout <<"\nTest copy constructor and copy assignment.\n";
Auto_ptr<int> p1(new int(10));
Auto_ptr<int> p2(p1);
std::cout <<"copy constructed p2 points to: "<< *p2 << "\n";
//assert(*p2, 10);
Auto_ptr<int> p3(new int(20));
p1 = p3;
std::cout <<"copy assigned p1 points to: "<< *p1 << "\n";
//assert(*p1, 20);
std::cout <<"TEST 3 DONE\n";
}
//=====================================================================
int main () {
test1 ();
test2 ();
test3 ();
getchar();
}
The interesting note is that in the Live example the error in not reproducible.
What could be the cause of this message ?
This is generally the result of a "double delete" - in this case I suspect it is your copy assignment.
You assign value = p.value; and both copies hold the same value and both attempt to delete it.
You essentially need to move the pointer from one object to the other, as is the case with the copy constructor. You will also need to remove the const as well.
Related
I created ABC class and created its three objects by normal, assign and copy constructor. Now they are use same memory address for ptr.
When these objects are deleted means coming out of scope then first object deleted, but for second it is give error that memory is already deleted.
This is fine. that I understand.
#include<iostream>
using namespace std;
class ABC
{
private:
int a;
int *ptr;
public:
ABC(); // Simple constructor.
ABC(int a, int b); // Parameterized constructor.
ABC(const ABC &obj); // Copy constructor.
~ABC(); // Destructor.
void display(); // Display.
ABC& operator=(const ABC &obj); // Operator Overload.
};
ABC::ABC()
{
}
ABC::ABC(int a, int b)
{
cout << "Parameterized constructor" << endl;
// allocate memory for the pointer;
this->a = a;
ptr = new int;
ptr = &b;
}
ABC::ABC(const ABC &obj)
{
cout << "Copy constructor" << endl;
a = obj.a;
//ptr = new int;
//*ptr = *obj.ptr; // copy the value
ptr = obj.ptr;
}
ABC& ABC :: operator=(const ABC &obj)
{
cout <<"Assignemnt operator overload"<<endl;
this->a = obj.a;
this->ptr = obj.ptr;
return *this;
}
ABC::~ABC(void)
{
cout << "Freeing memory!" << endl;
delete ptr;
}
void ABC::display() {
cout << "a value = : " << a <<endl;
cout << "ptr value = : " << ptr <<endl;
cout << "*ptr value = : " << *ptr <<endl;
}
int main()
{
// Normal.
ABC obj1(1, 2);
cout << "Point Obj1 value = : "<<endl;
obj1.display();
cout<<"\n\n";
// Assignment.
ABC obj2;
obj2 = obj1;
cout << "Point Obj2 value = : "<<endl;
obj2.display();
cout<<"\n\n";
// Copy constructor.
ABC obj3(obj1);
cout << "Point Obj3 value = : "<<endl;
obj3.display();
return 0;
}
What I want to do it that, I do not want to delete memory when other objects are using. How to handle this by Smart Pointer, I not want to do by in-build sheared pointer. I want to write Smart Pointer class and increase ptr reference count when other objects use same memory. But do not know how to do.
class SmartPointer
{
public:
int *ptr;
int ref;
SmartPointer();
SmartPointer(int *p);
int& operator *();
~SmartPointer();
};
SmartPointer::SmartPointer()
{
cout<<"SmartPointerInitilaize default"<<endl;
ref = 1;
}
SmartPointer::SmartPointer(int *p)
{
cout<<"SmartPointerInitilaize para"<<endl;
ptr = p;
ref = 1;
}
int& SmartPointer:: operator *()
{
return *ptr;
}
SmartPointer::~SmartPointer()
{
cout<<"SmartPointer De-Initilaize"<<endl;
//delete ptr;
}
What you basically want to do is to implement a std::shared_ptr. You shouldn't do it normally, because it is quite tricky, however for educational purposes and to understand how that works:
1) The ref count needs to be part of the pointer data passed around (if not static), shared by all the "linked" SmartPointer instances.
2) You still need to define the copy constructor/assignment operator to increase the reference count. And in the destructor you decrease the refcount, and if zero, delete the pointer (and the extra data).
An example:
class SmartPointer
{
struct Data {
Data(int *p)
: ptr(p)
, ref(1)
{}
~Data() {Release();}
void Acquire() {
++ref;
}
void Release() {
if (!--ref) { delete ptr; delete this; }
}
int *ptr;
int ref;
};
Data *data;
public:
SmartPointer()
: data(new Data(NULL))
{}
SmartPointer(int *p)
: data(new Data(p))
{}
SmartPointer(const SmartPointer& x)
: data(x.data)
{ data->Acquire(); }
SmartPointer& operator =(const SmartPointer& x)
{
if (this != &x) {
data->Release();
data = x.data;
data->Acquire();
}
}
int& operator *() { return *data->ptr; }
~SmartPointer() { data->Release(); }
};
Note that this is very simplified (e.g. not thread safe), just the basic idea.
The actual std or boost shared_ptr is much more complicated (templated, supports custom deleter which involves type erasure etc.).
Add shared_level integer in SmartPointer
class SmartPointer{
public:
int *ptr;
int ref;
int shared_level;
SmartPointer();
SmartPointer(int *p);
int& operator *();
~SmartPointer();
};
Whenever the abc constructor is calling. Increment shared_level by 1. When ever deconstructor calls, Decrement it by 1.
And While deconstructing check for SmartPointer->shared_level value, And if it is 1. Delete pointer, else just decrements is enough.
Note : Better use locks for sharePointer, If u want to access in multiple threads.
I want to create member variable sptr of ABC which is object of other class SmartPointer. And then assign value to that member variable.
#include<stdio.h>
#include<iostream>
using namespace std;
class SmartPointer
{
private:
int *ptr;
public:
SmartPointer(int *p);
int& operator *();
~SmartPointer();
};
SmartPointer::SmartPointer(int *p = NULL)
{
cout<<"Initilaize SmartPointer"<<p<< endl;
ptr = p;
}
int& SmartPointer:: operator *()
{
return *ptr;
}
SmartPointer::~SmartPointer()
{
cout<<"De-Initilaize SmartPointer"<<endl;
delete ptr;
}
class ABC
{
private:
int a;
SmartPointer *sptr;
int ref;
public:
ABC(); // Simple constructor.
ABC(int a, int b); // Parameterized constructor.
// ABC(const ABC &obj); // Copy constructor.
~ABC(); // Destructor.
void display(); // Display.
// ABC& operator=(const ABC &obj); // Operator Overload.
};
ABC::ABC()
{
ref= 1;
}
ABC::ABC(int a, int b)
{
cout << "Parameterized constructor input" << endl;
// allocate memory for the pointer;
sptr = new SmartPointer(&a);
cout << "Parameterized constructor Out"<<sptr << endl;
}
ABC::~ABC(void)
{
cout << "Freeing memory!" << endl;
ref --;
if(ref==0)
{
//delete sptr;
}
}
void ABC::display()
{
}
int main()
{
// int a = 10;
// SmartPointer obj1(&a);
// Normal.
ABC obj2(1, 2);
return 0;
}
created sptr = new SmartPointer(&a);, but how to get value of sptr ?
Please have a look at the code below. Is this a smart pointer?
If so, why the first object, p1, is dangling at the end of the code? (That is p2 is deleted by the destructor but p1 remains, why?)
#include <iostream>
#include <vector>
using namespace std;
template <class T> class my_auto_ptr {
T* myptr;
public:
my_auto_ptr(T* ptr = 0) : myptr(ptr) { }
~my_auto_ptr() {
delete myptr;
}
T* operator ->() const {
if (myptr != nullptr) return myptr;
else throw runtime_error("");
}
T& operator* () const {
if (myptr != nullptr) return *myptr;
else throw runtime_error("");
}
T* release() {
T* rptr = myptr;
myptr = 0;
return rptr;
}
};
//----------------------------------
int main() try {
my_auto_ptr<vector<int> > p1(new vector<int>(4, 5));
cout << p1->size() << endl;
my_auto_ptr<int> p2(new int(6));
cout << *p2 << endl;
return 0;
}
//-------------------------------
catch (...) {
cerr << "Exception occurred.\n";
return 1;
}
Is this a smart pointer?
No. It is copyable and assignable, but performing either of those operations will result in multiple deletes. You need to make sure that it is either non-copyable and non-assignable, or that it implements the rule of 3 or 5.
Someone knows why the Latter syntax for calling , Dog::operator new is calling the default constructor after doing the allocation, ending up calling 2 constructors ?
I want to know if I'm doing something wrong, and how can I do:
Dog *ptr = new("arg") Dog();
without calling 2 constructors. and without using any trick, like checking in the default constructor if the object is already constructed. Here is the code:
class Dog
{
public:
Dog() // default
{
std::cout << "default Dog constructor [" << this << "]" << std::endl;
}
Dog(int x) // int argument
{
std::cout << "dog constructor int " << x << "[" << this << "]" << std::endl;
}
Dog(const std::string& word) // std::string argument
{
std::cout << "dog constructor std::string: " << word << " ["<< this << "]" << std::endl;
}
Dog(std::string &&word) // rvalue string argument
{
std::cout << "dog constructor std::string&& rvalue: " << word << " [" << this << "]" << std::endl;
}
// custom operator new
static void *operator new(std::size_t size) noexcept // for default constructor
{
Dog *ptr = (Dog*)malloc(size); // allocate memory
if (ptr) // if allocate ok
{
::new(ptr) Dog(); // call default constructor on object in memory
return ptr; // returns
}
else
return nullptr;
}
template<class T>
static void * operator new(std::size_t size, T&& value) noexcept // for argument constructor
{
Dog *ptr = (Dog*) malloc(size); // allocate the memory
if (ptr)
{
::new (ptr) Dog(std::forward<T>(value)); // pass the argument exactly as was passed to operator new,
// using perfect forwarding
return ptr;
}
else
return nullptr;
}
~Dog() { std::cout << "destructor " << std::endl; }
};
int main(void)
{
Dog *d = (Dog*) Dog::operator new(sizeof(Dog), "Const Char * Argument"); // argument version
Dog *d2 = (Dog*)Dog::operator new(sizeof(Dog)); // default constructor argument
//1 this works as expected, do what you specified in the member operator new, everything goes normal.
Dog *d3 = new Dog(); // default constructor
Dog *d4 = new("Const Char * Argument") Dog(); // argument constructor
// this is shorter, goes into your member operator new, BUT when it returns to this scope,
// call the default constructor for *d3, and for *d4 too.
// so this ends up calling constructors twice for both objects.
}
So, I was mixing allocation with construction, there's no reason for doing that here, maybe there is some use for that in operator new[] to construct the array with a contructor other than the default contructor.
But the the best way to define these members operators are:
class Dog {
public:
// .......
// custom operator new
static void *operator new(std::size_t size) noexcept // for default constructor
{
void *memory = malloc(size); // allocate memory
if (memory) // if allocate ok
{
return memory; // returns
}
else
return nullptr;
}
static void *operator new[](std::size_t size) noexcept
{
void *memory = malloc(size); // allocate memory
if (memory) // if allocate ok
{
return memory; // returns
}
else
return nullptr;
}
static void operator delete(void *block) noexcept
{
free(block);
}
static void operator delete[](void *block) noexcept
{
free(block);
}
~Dog() { std::cout << "destructor " << std::endl; }
};
int main(void)
{
// now we can use new operator normaly without complications
Dog *d1 = new Dog[10]; // default constructor on all objects
Dog *d2 = new Dog("const char * argument"); // call std::string&& constructor
delete[] d1;
delete d2;
}
Using (keyword) new: It invokes the allocating operator new and (!) calls the constructor.
Note: When providing an operator new you should provide an operator delete, too (in your case it would call free). Also, do not forget the array versions.
despite the ocean of smart pointer questions out there, I seem to be stuck with one more. I am trying to implement a ref counted smart pointer, but when I try it in the following case, the ref count is wrong. The comments are what I think should be the correct ref counts.
Sptr<B> bp1(new B); // obj1: ref count = 1
Sptr<B> bp2 = bp1; // obj1: ref count = 2
bp2 = new B; // obj1: ref count = 1, obj2: rec count = 1 **problem**
In my implementation, my obj2 ref count is 2, because of this code:
protected:
void retain() {
++(*_rc);
std::cout << "retained, rc: " << *_rc << std::endl;
}
void release() {
--(*_rc);
std::cout << "released, rc: " << *_rc << std::endl;
if (*_rc == 0) {
std::cout << "rc = 0, deleting obj" << std::endl;
delete _ptr;
_ptr = 0;
delete _rc;
_rc = 0;
}
}
private:
T *_ptr;
int *_rc;
// Delegate private copy constructor
template <typename U>
Sptr(const Sptr<U> *p) : _ptr(p->get()), _rc(p->rc()) {
if (p->get() != 0) retain();
}
// Delegate private assignment operator
template <typename U>
Sptr<T> &operator=(const Sptr<U> *p) {
if (_ptr != 0) release();
_ptr = p->get();
_rc = p->rc();
if (_ptr != 0) retain();
return *this;
}
public:
Sptr() : _ptr(0) {}
template <typename U>
Sptr(U *p) : _ptr(p) { _rc = new int(1); }
// Normal and template copy constructors both delegate to private
Sptr(const Sptr &o) : Sptr(&o) {
std::cout << "non-templated copy ctor" << std::endl;
}
template <typename U>
Sptr(const Sptr<U> &o) : Sptr(&o) {
std::cout << "templated copy ctor" << std::endl;
}
// Normal and template assignment operator
Sptr &operator=(const Sptr &o) {
std::cout << "non-templated assignment operator" << std::endl;
return operator=(&o);
}
template <typename U>
Sptr<T> &operator=(const Sptr<U> &o) {
std::cout << "templated assignment operator" << std::endl;
return operator=(&o);
}
// Assignment operator for assigning to void or 0
void operator=(int) {
if (_ptr != 0) release();
_ptr = 0;
_rc = 0;
}
The constructor is initialized with a ref count = 1, but in my assignment operator, I am retaining the object, making the ref count = 2. But it should only be 1 in this case, because bp2 = new B is only one pointer to that object. I've looked over various smart pointer implementation examples, and I can't seem to figure out how they deal with this case that I'm having trouble with.
Thanks for your time!
The assignment operator is riddled with small errors and unnecessary complex anyway. For example, when you assign Sptr<T> to itself it will have funny effects. Most manually written assignment operators should look like this:
T& T::operator= (T const& other) {
T(other).swap(*this);
return *this;
}
... or
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
Once stateful allocators enter the game things change a bit but we can ignore this detail here. The main idea is to leverage the existing work done for the copy constructor and the destructor. Note, that this approach also works if the right hand side isn't T, i.e., you can still leverage a corresponding constructor, the destructor, and swap(). The only potentially additional work is desirable anyway, and trivial to implement: the swap() member. In your case that is very simple, too:
template <typename T>
void Sptr<T>::swap(Sptr<T>& other) {
std::swap(this->_ptr, other._ptr);
std::swap(this->_rc, other._rc);
}
Just a note on your assignment operator taking an int: This is a very bad idea! To reset the pointer, you should probably better have a reset() method. In C++ 2011 you could reasonably have a method taking a std::nullptr_t for this purpose, though.
Define an assignment operator for the raw pointer type. Otherwise, it will construct a new smart pointer with ref count 1, which you will then increase to 2.
Example:
template <typename U>
Sptr<T> &operator=(U *p)
{
if (_ptr != 0)
{
_ptr = p;
_rc = new int(1);
}
return *this;
}
This stuff is notoriously tricky, and there are probably more bugs waiting. I would make the constructor explicit to prevent other unintentional constructions.
From what I see of your code, you need a proper destructor in your smart pointer class to call release and fix the counter. You need at least that modification for your counter to work correctly.
I didn't see a Sptr(T *) constructor in your code, did you omit it?
As a side note, I would probably use a std::pair to store the counter and the T pointer, but your data layout works as it is. As another answer pointed it out, you should pay attention to the self assignment case though.
Here's a minimal implementation following your data layout and interface choices:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Sptr {
protected:
T *_ptr;
int *_rc;
virtual void retain() {
if (_rc) // pointing to something
++(*_rc);
clog << "retain : " << *_rc << endl;
}
virtual void release() {
if (_rc) {
--(*_rc);
clog << "release : " << *_rc << endl;
if (*_rc == 0) {
delete _ptr;
_ptr = NULL;
delete _rc;
_rc = NULL;
}
}
}
public:
Sptr() : _ptr(NULL), _rc(NULL) {} // no reference
virtual ~Sptr() { release(); } // drop the reference held by this
Sptr(T *p): _ptr(p) { // new independent pointer
_rc = new int(0);
retain();
}
virtual Sptr<T> &operator=(T *p) {
release();
_ptr = p;
_rc = new int(0);
retain();
return *this;
}
Sptr(Sptr<T> &o) : _ptr(o._ptr), _rc(o._rc) {
retain();
}
virtual Sptr<T> &operator=(Sptr<T> &o) {
if (_rc != o._rc){ // different shared pointer
release();
_ptr = o._ptr;
_rc = o._rc;
retain();
}
return *this;
}
};
int main(){
int *i = new int(5);
Sptr<int> sptr1(i);
Sptr<int> sptr2(i);
Sptr<int> sptr3;
sptr1 = sptr1;
sptr2 = sptr1;
sptr3 = sptr1;
}
_rc is the indicator in your class that a pointer is shared with another instance or not.
For instance, in this code:
B *b = new B;
Sptr<B> sptr1(b);
Sptr<B> sptr2(b);
sptr1 and sptr2 are not sharing the pointer, because assignments were done separately, and thus the _rc should be different, which is effectively the case in my small implementation.