Ref counted smart pointer's assignment operator - c++

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.

Related

Why is move constructor called when doing assignment?

I'm trying to make a sample implementation of std::unique_ptr. Here is the code:
include <iostream>
template<typename T>
class UniquePtr
{
public:
explicit UniquePtr(T * ptr)
:m_ptr(ptr)
{};
UniquePtr(UniquePtr const & other) = delete;
UniquePtr& operator=(UniquePtr const & other) = delete;
explicit UniquePtr(UniquePtr && other)
{
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
}
UniquePtr & operator=(UniquePtr && other)
{
std::cout << "Move assignment called " << std::endl;
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
}
~UniquePtr()
{
delete m_ptr;
}
T& operator*()
{
return *m_ptr;
}
T& operator->()
{
return m_ptr;
}
private:
T * m_ptr = nullptr;
};
int main()
{
UniquePtr<int> t(new int(3));
t= UniquePtr<int>(new int(4));
std::cout << *t << std::endl;
}
This code compiles and I'm able to see the value 4 in the output even after deleting the default assignment and copy constructor. What am I doing wrong?
In the assignment, move is called because the UniquePtr<int>(new int(4)) is constructing a temporary object, and in this case the compiler tries to use the move assignment if possible, else it would fall back to the copy assignment, which is deleted.
UniquePtr<int> t(new int(3));
t= UniquePtr<int>(new int(4)); // move assignment because temporary
auto k = t; // copy assignment since t is not temporary and so does not compile.
As commented, the move assignment is not returning *this, you should enable all warnings. Also the last operator-> has a syntax error, it returns a pointer but expects a reference.
Additionally, your code has a major issue, in case of exceptions you could have a memory leak. Suppose you write something like:
class Banana
{ ... }
void eatBanana(UniquePtr<Banana> banana, int amountToEat);
int computeAmount();
eatBanana(UniquePtr<Banana>(new Banana(3)), computeAmount());
If, for any reason, the computeAmount() function throws an exception, then the memory allocated by new could never be released, because computeAmount() could be executed between new and the constructor of the UniquePtr. For this reason, we normally use std::make_unique().
You should implement your own version of make_unique() and use it, it's trival, see here: https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
Here more information about the issues and the solution:
https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch04.html

How to return an object of a class which is derived from an abstract class?

So, I have a generic operator. In this specific case its generic template T is supposed to be a pointer to an abstract base class. The point is, when I create T *t and try to return *t I get an error saying T can't be instantiated because it's an abstract class. Even though *t will always be an object of derived classes which aren't abstract. It doesn't accept it, because T is after all abstract type. Basically, what I want to achieve is to get a reference to the *t returned from the operator to main() and then assign to it objects of derived classes in main().
I understand that there are questions about slicing and polymorphism. But I am trying to understand how can I achieve it in different ways. It's a completely different question which deals with the following specific code.
Code is something like this:
Template <class T>
class myArr{
T & operator [] (int index){
T*t;
Arr[0] = *t;
Return arr[0[;
}}
Main(){
Base * b=new derived;
myArr<Base>[17]=*b;
// Point is, I can make it of type pointers and maybe it can
//work but I prefer assigning *b to myArr because I want
to //assign objects themselves
}
My question is, how can I return the object in itself from the operator? I want to return something to which I can assign a derived object . I am trying to do it with pointer of type abstract base, so as to achieve polymorphism so it can contain any object of derived classes.
You cannot return an object of an abstract class. You need to return a reference or a pointer, preferably a smart one.
You cannot store an object of an abstract class. You need to store a reference or a pointer, preferably a smart one.
You cannot pass an object of an abstract class to a function as a parameter. You need to pass a reference or a pointer, preferably a smart one.
See the pattern?
Objects of abstract classes do not exist on their own. They only exist as subobjects of derived class objects, and can only be accessed via references or pointers obtained from pointers to said derived class objects.
There is no way around it. You cannot do it differently. Use smart pointers. They are the right tool for tthe job.
I want to return something to which I can assign a derived object .
This is kind of possible, depending on what exactly you want, but way more complicated than necessary. Assignment and polymorphic hierarchies don't mix well. I would not recommend that, especially if you are not sure what exactly you want. Assign, store, and pass around smart pointers.
I tried making something out of the code you showed by using std::unique_ptr<Base> in your array. It shows how you can replace objects in the array (base_arr[0] = ...) and how to update existing objects in the array (*base_arr[0] = ...). I added two derived classes carrying different types (a std::string and an int) and a lot of debug prints so it's easier to follow what's happening when you run it.
#include <iostream>
#include <vector>
#include <memory>
#include <string>
// Your container wrapper with some functions
template<class T>
class myArr {
public:
using type = T;
template<class... Args>
decltype(auto) emplace_back(Args&&... args) {
return arr.emplace_back(std::forward<Args>(args)...);
}
T& operator[](std::size_t index) { return arr[index]; }
auto begin() const { return arr.begin(); }
auto end() const { return arr.end(); }
T extract_front() {
T retval(std::move(arr.front()));
arr.erase(arr.begin());
return retval;
}
private:
std::vector<T> arr{};
};
//----------------------------------------------------------------------------------
struct Base {
Base() = default;
Base(const Base&) = default;
Base(Base&&) = default;
virtual ~Base() = 0;
virtual Base& operator=(const Base&) = 0;
virtual Base& operator=(Base&&) = 0;
virtual void print() = 0;
};
Base::~Base() {}
Base& Base::operator=(const Base&) {
std::cout << "Base& Base::operator=(const Base&)\n"; // nothing real to copy here
return *this;
}
Base& Base::operator=(Base&&) {
std::cout << "Base& Base::operator=(Base&&)\n"; // nothing real to move here
return *this;
}
//----------------------------------------------------------------------------------
struct der_1 : public Base {
der_1(const std::string& value) : Base(), m_value(value) {
std::cout << "der_1(" << m_value << ") converting\n";
}
der_1(const der_1& rhs) : Base(rhs), m_value(rhs.m_value) {
std::cout << "der_1(" << m_value << ") copy\n";
}
der_1(der_1&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
std::cout << "der_1(" << m_value << ") move\n";
}
~der_1() { std::cout << "~der_1(" << m_value << ")\n"; }
der_1& operator=(const der_1& rhs) {
std::cout << "der_1& der_1::operator=(const der_1&)\n";
if(this == &rhs) return *this; // no self-assignment
Base::operator=(rhs); // copy the Base part of rhs
m_value = rhs.m_value; // copy the der_1 specific part
return *this;
}
der_1& operator=(der_1&& rhs) {
std::cout << "der_1& der_1::operator=(der_1&&)\n";
Base::operator=(std::move(rhs)); // move the Base part of rhs
m_value = std::move(rhs.m_value); // move the der_1 specific part
return *this;
}
// override Base's copy assignment
Base& operator=(const Base& rhs) override {
std::cout << "Base& der_1::operator=(const Base&)\n";
// downcasting may throw bad_cast
const der_1& rhsref = dynamic_cast<const der_1&>(rhs);
return *this = rhsref; // call standard copy assignment
}
// override Base's move assignment
Base& operator=(Base&& rhs) override {
std::cout << "Base& der_1::operator=(Base&&)\n";
// downcasting may throw bad_cast
der_1& rhsref = dynamic_cast<der_1&>(rhs);
return *this = std::move(rhsref); // call standard move assignment
}
void print() override { std::cout << "der_1::print(" << m_value << ")\n"; }
private:
std::string m_value;
};
//----------------------------------------------------------------------------------
struct der_2 : public Base {
der_2(int value) : Base(), m_value(value) {
std::cout << "der_2(" << m_value << ") converting\n";
}
der_2(const der_2& rhs) : Base(rhs), m_value(rhs.m_value) {
std::cout << "der_2(" << m_value << ") copy\n";
}
der_2(der_2&& rhs) : Base(std::move(rhs)), m_value(std::move(rhs.m_value)) {
std::cout << "der_2(" << m_value << ") move\n";
}
~der_2() { std::cout << "~der_2(" << m_value << ")\n"; }
der_2& operator=(const der_2& rhs) {
std::cout << "der_2& der_2::operator=(const der_2&)\n";
if(this == &rhs) return *this; // no self-assignment
Base::operator=(rhs); // copy the Base part of rhs
m_value = rhs.m_value; // copy the der_2 specific part
return *this;
}
der_2& operator=(der_2&& rhs) {
std::cout << "der_2& der_2::operator=(der_2&&)\n";
Base::operator=(std::move(rhs)); // move the Base part of rhs
m_value = std::move(rhs.m_value); // move the der_2 specific part
return *this;
}
// override Base's copy assignment
Base& operator=(const Base& rhs) override {
std::cout << "Base& der_2::operator=(const Base&)\n";
// downcasting may throw bad_cast
const der_2& rhsref = dynamic_cast<const der_2&>(rhs);
return *this = rhsref; // call standard copy assignment
}
// override Base's move assignment
Base& operator=(Base&& rhs) override {
std::cout << "Base& der_2::operator=(Base&&)\n";
// downcasting may throw bad_cast
der_2& rhsref = dynamic_cast<der_2&>(rhs);
return *this = std::move(rhsref); // call standard move assignment
}
void print() override { std::cout << "der_2::print(" << m_value << ")\n"; }
private:
int m_value;
};
//----------------------------------------------------------------------------------
int main() {
myArr<std::unique_ptr<Base>> base_arr;
{
{
std::cout << "-- put pointers to objects of derived classes in base_arr --\n";
base_arr.emplace_back(std::make_unique<der_1>("howdy"));
base_arr.emplace_back(std::make_unique<der_2>(10));
std::cout << "\n-- print what we've got --\n";
for(auto& b : base_arr) b->print();
std::cout << "\n-- set new value for an existing object, by copying --\n";
der_1 something_to_copy("something_to_copy");
*base_arr[0] = something_to_copy;
std::cout << "\n-- set new value for an existing object, by moving --\n";
*base_arr[0] = der_1("something_to_move");
std::cout << "\n-- try to assign a der_2 to a der_1 --\n";
try {
*base_arr[0] = der_2(666);
} catch(const std::exception& ex) {
std::cout << "Exception: " << ex.what() << "\n";
}
std::cout << "\n-- replace a der_1 object with a der_2 object --\n";
base_arr[0] = std::make_unique<der_2>(20);
std::cout << "\n-- destroying something_to_copy since it goes out of "
"scope --\n";
}
std::cout << "\n-- stuff in base_arr --\n";
for(auto& b : base_arr) b->print();
std::cout << "\n-- extract front, got:\n";
auto ptr = base_arr.extract_front();
ptr->print();
std::cout << "\n-- the above dies, goes out of scope --\n";
}
std::cout << "\n-- base_arr is about to be destroyed --\n";
}

Do not delete ptr when momery is used by other objects

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.

Dereferencing not working for smart pointer in set

#include<iostream>
#include<set>
template <typename T>
/* Simple smart pointer class */
class SmartPtr
{
T *ptr;
public:
explicit SmartPtr(T *p = NULL) { ptr = p; }
~SmartPtr() { delete(ptr); }
T & operator * () { return *ptr; }
T * operator -> () { return ptr; }
};
class simple {
private:
int x;
public:
simple(int y = 0) :x(y) {}
int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;
int main() {
std::set<simplePtr> st;
simplePtr p1 = simplePtr(new simple(5));
simplePtr p2 = simplePtr(new simple(5));
simplePtr p3 = simplePtr(new simple(5));
simplePtr p4 = simplePtr(new simple(5));
std::cout << p1->getX(); <-- working fine
st.insert(p1);
st.insert(p2);
st.insert(p3);
st.insert(p4);
for (std::set<simplePtr>::iterator it = st.begin(); it != st.end(); ++it)
{
std::cout << it->getX(); // Not working??
}
}
Compilation is failed with error in Visual Studio 2013:
Error C2039 getX: is not a member of SmartPtr<simple>
On linux:
error: ‘const class SmartPtr<simple>’ has no member named ‘getX’
Is this a problem with iterator??
You can think of it->getX() as a syntactic sugar for (*it).getX(). [In principle, a class can overload the -> and * (dereferencing) operators inconsistently, but std::set<T>::iterator, unsurprisingly, doesn't break that convention]. So, in your case, *it is dereferenced to an lvalue of type const SmartPtr<simple>&, and the .getX() applied to it fails, because SmartPtr doesn't have a getX() method. Since, instead you mean to access the object that the obtained SmartPtr points to, you must add one more level of dereferencing:
Correction 1
Replace it->getX() with (**it).getX() or (*it)->getX().
There is still another problem, though - *it results in a const SmartPtr (yes, std::set's non-constant iterator doesn't provide write access to the container's elements, otherwise you could break correct ordering of elements in the container). But both -> and * (dereferencing) operators in SmartPtr are defined in such a way that they can be invoked only on non-const objects. To fix that, you must make those two functions const:
Correction 2 (in SmartPtr<T>)
// vvvvv
T & operator * () const { return *ptr; }
T * operator -> () const { return ptr; }
// ^^^^^
After you make this second correction, you can replace your old-style for-loop with a range-for loop:
for (const simplePtr& p : st)
{
std::cout << p->getX();
}
Still, your program will not compile - SmartPtr<T> objects cannot be put in an std::set since they are not comparable. Fix that by defining operator<():
Correction 3
Add to SmartPtr<T>:
bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
At this point your code will compile but chances are high that it will not work correctly. The reason is that the copy-semantics of SmartPtr<T> is left to compiler's discretion which fails to meet your intent. This is easy to guess by spotting the violation of the Rule of Three, Four and Five - your class defines the destructor but fails to define the copy and/or move constructor and the assignment operator. As a result your code performs double deletion and therefore cannot be guaranteed any well defined behavior.
Correction 4
Fix the copy semantics of SmartPtr<T>.
I "fixed" your code by assigning move semantics to SmartPtr (this required adding std::move() when insert()-ing it into std::set):
#include<iostream>
#include<set>
template <typename T>
class SmartPtr
{
T *ptr;
public:
explicit SmartPtr(T *p = NULL) { ptr = p; }
~SmartPtr() { delete(ptr); }
SmartPtr(const SmartPtr& other) = delete;
SmartPtr(SmartPtr&& other) : ptr(other.ptr) { other.ptr = NULL; }
SmartPtr& operator=(SmartPtr other)
{
std::swap(ptr, other.ptr);
return *this;
}
T & operator * () const { return *ptr; }
T * operator -> () const { return ptr; }
bool operator<(const SmartPtr& other) const { return ptr < other.ptr; }
};
class simple {
int x;
public:
simple(int y = 0) : x(y) {}
int getX() { return x; }
};
typedef SmartPtr<simple> simplePtr;
int main() {
std::set<simplePtr> st;
simplePtr p1 = simplePtr(new simple(5));
simplePtr p2 = simplePtr(new simple(5));
st.insert(std::move(p1));
st.insert(std::move(p2));
for (const simplePtr& p : st)
{
std::cout << p->getX();
}
return 0;
}
Your iterator needs to be dereferenced, at which point you get a pointer. Which then needs to be dereferenced. So:
std::cout << (*it)->getX();
For starters you have to define operator < for the class SmartPtr before using it in the set.
Secondly you have to declare the member function getX like
int getX() const { return x; }
And you have to write at least like
std::cout << ( *it )->getX();

smart pointer implementation for derived to base pointers

I have created a smart pointer implementation as given below ::
#include <iostream>
#include <vector>
using namespace std;
class Obj {
int i, j;
public:
Obj() { i = 0 ; j = 0 ; }
Obj(int ii , int jj) : i(ii) , j(jj) { }
void f() { cout << i << endl; }
void g() { cout << j << endl; }
};
class ObjDerived : public Obj
{
int k;
public:
ObjDerived(int kk = 0) :k(kk) { }
void h() { cout << k << endl; }
};
template <typename ULT >
class SP
{
ULT* ptr;
public:
explicit SP(ULT* tptr = NULL) : ptr(tptr) { }
template <typename OTHER>
SP(SP<OTHER>& other)
{
ptr = (ULT*)other.ptr;
}
ULT* operator->() { return ptr; }
ULT& operator*() { return *ptr; }
SP<ULT>& operator=(SP<ULT>& tptr)
{
if(ptr != tptr.ptr)
ptr = tptr.ptr;
return *this;
}
SP<ULT>& operator=(ULT* tptr)
{
ptr = tptr;
return *this;
}
template <typename OTHER>
SP<ULT>& operator=(SP<OTHER>& der) // ??
{
cout << "In operator\n";
this->ptr = (ULT*)der.ptr;
return *this;
}
~SP()
{
if(ptr != NULL )
delete ptr;
}
};
int main()
{
SP<Obj> Sptr2(new Obj(10,20));
SP<ObjDerived> Sptr4(new ObjDerived(80));
Sptr2 = Sptr4; //error in this line
return 0;
}
I am trying to cast a derived class pointer to base class pointer using smart pointer SP . The operator= member function
template <typename OTHER>
SP<ULT>& operator=(SP<OTHER>& der) // ??
{
cout << "In operator\n";
this->ptr = (ULT*)der.ptr;
return *this;
}
is giving the following error ---> error: 'ObjDerived* SP::ptr' is private
I am not able to find out how to achieve the desired pointer conversion using smart pointer. I also searched previous posts on smart pointers but could not find exact answer to my problem.
You can provide public get() function member to be able to retrieve raw pointer. Commonly smart pointers provide this functionality. Small example:
template <typename ULT >
class SP {
// ...
ULT* get() const {return ptr;}
template <typename OTHER>
SP<ULT>& operator=(SP<OTHER>& der) {
cout << "In operator\n";
this->ptr = (ULT*)der.get();
return *this;
}
};
Also you can see Item 45 of Effective C++ for more information about creating smart pointer.
I assumed you are doing this for training tasks, because there are quite good smart pointers implementation, for example, boost::shared_ptr.
Well the member ptr is private - make it public or provide a public access method.
Have you considered using Boost smart pointers instead of attempting to roll your own? They've already gone through all the pain of implementing good smart pointer classes. Even if for some reason you can't use Boost, you can still learn a lot from their classes.
You can make member ptr as private. Or you can use in-built copy mechanism:
Instead of,
this->ptr = (ULT*)der.ptr; // ptr needs to be accessible
Do following:
*this = *((ULT*&)der); // copy using in-built shallow copy