SmartPointer in C++ using template - c++

I am trying to implement a smart pointer (basically unique pointer) in C++ using templates for my understanding.
This is what I have coded
using namespace std;
template<typename T>
class smartPointer
{
private:
T *mPtr;
public:
smartPointer(T* init=nullptr):mPtr(init)
{
cout<<"Inside ctr"<<endl;
}
//silence the default ctr
smartPointer() = delete;
//disable copy ctr and copy assignment
smartPointer (const smartPointer& other) = delete;
smartPointer& operator=(const smartPointer& other) = delete;
//implement move ctr and move assignment
smartPointer (smartPointer&& other)
{
cout<<"Inside move ctr "<<endl;
mPtr = other.mPtr;
other.mPtr = nullptr;
}
smartPointer& operator=(smartPointer&& other)
{
cout<<"Inside move = before "<<endl;
if(&other != this)
{
mPtr = other.mPtr;
other.mPtr = nullptr;
cout<<"Inside move = after "<<endl;
}
return *this;
}
//deference
T& operator*()
{
return *mPtr;
}
//arrow access operator
T* operator->()
{
return mPtr;
}
~smartPointer()
{
cout<<"Inside ~dtr"<<endl;
if(mPtr != nullptr)
delete mPtr;
}
};
int main()
{
smartPointer<int> sptr(new int);
// Even smartPointer<int> sptr(new int[20]); too works
*sptr = 10;
cout<<"Value pointed by the pointer is "<<*sptr<<endl;
smartPointer<int> sptr2(new int);
// sptr2 = sptr; // complains as expected
sptr2 = move(sptr); // works well
/*
How to
smartPointer<int[]> sptr(new int[20]);
*/
return 0;
}
How to make this smart pointer to work for smartPointer ?
The build command I give is
g++ -std=c++11 -smartPointer.cpp -o smartPointer

You can add operator[] to the base template, and use SFINAE to ensure it only compiles when the type is an array type. You would do the same to make sure operaotr* and operaotr-> only compile for the non-array version. Then, tag dispatch can be used to call the correct delete expression.
template<typename T>
class smartPointer
{
private:
T *mPtr;
del_member(std::true_type) { delete[] mPtr; }
del_member(std::false_type) { delete mPtr; }
public:
// Removed you code for brevity.
//deference
auto operator*() -> std::enable_if_t<!std::is_array<T>::value, T&>
{
return *mPtr;
}
//arrow access operator
auto operator->() -> std::enable_if_t<!std::is_array<T>::value, T*>
{
return mPtr;
}
auto operator[](std::size_t idx) -> std::enable_if_t<std::is_array<T>::value, T&>
{
return mPtr[idx];
}
~smartPointer()
{
del_member(std::is_array<T>{});
}
};
The above is perfectly legal, and can exist in the same template. Remember the members of a template are instantiated only when used. So unless users of your class try to use an operator which the underlying pointer shouldn't support, they will get no errors.
If your compiler doesn't support c++14 type traits yet, the above trailing return types can be turned to the form:
typename std::enable_if<CONDITION, TYPE>::type
For your c'tor, I recommend you don't hard-code T*. Instead add the following type alias to your class, and change the c'tor accordingly.
using ptr_type = std::conditional_t<std::is_array<T>::value, std::decayt_t<T>, std::add_pointer_t<T>>;
smartPointer(ptr_type init)

Related

How to redefine the template class constructor via a macro in C++11?

I want to recorded the line which created the shared_ptr in C++ 11.
Here is my way to rewrite shared_ptr as Shared_ptr :
template<class T>
class Shared_Ptr{
public:
Shared_Ptr(T* ptr = nullptr,int line=__LINE__)
:_pPtr(ptr)
, _pRefCount(new int(1))
, _pMutex(new mutex)
{
cout<<this<<"is located in "<<line<<endl;
}
~Shared_Ptr()
{
Release();
cout<<this<<endl;
}
Shared_Ptr(const Shared_Ptr<T>& sp)
:_pPtr(sp._pPtr)
, _pRefCount(sp._pRefCount)
, _pMutex(sp._pMutex)
{
AddRefCount();
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp)
{
//if (this != &sp)
if (_pPtr != sp._pPtr)
{
Release();
_pPtr = sp._pPtr;
_pRefCount = sp._pRefCount;
_pMutex = sp._pMutex;
AddRefCount();
}
return *this;
}
T& operator*(){
return *_pPtr;
}
T* operator->(){
return _pPtr;
}
int UseCount() { return *_pRefCount; }
T* Get() { return _pPtr; }
void AddRefCount()
{
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
private:
void Release()
{
bool deleteflag = false;
_pMutex->lock();
if (--(*_pRefCount) == 0)
{
delete _pRefCount;
delete _pPtr;
deleteflag = true;
}
_pMutex->unlock();
if (deleteflag == true)
delete _pMutex;
}
private:
int *_pRefCount;
T* _pPtr;
mutex* _pMutex;
};
class student
{
int age;
public:
student(int a):age(a)
{
}
}
;
int main()
{
Shared_ptr<student> Tom(new student(24),__LINE__);
}
Is there a way to make Shared_ptr<student>Tom(new student(24)) as same as Shared_ptr <student> Tom(new student(24),__ LINE__) in C++11? In other words , invoke class Constructor with the arguments bound to args.
I tried to use marco to achieve,but I don't know the correct way how to define the macro of template class constructor.
Below is the macro definition I tried to write but wrong
template<typename T>
#define Shared_ptr<T>::Shared_ptr(T*) Shared_ptr<T>::Shared_ptr(T * ,__LINE__)
Replace int line=__LINE__ in constructor parameters with int line = __builtin_LINE(). It's a non-standard compiler extension, but it works at least in GCC, Clang, and MSVC (i.e. most common compilers).
Then Shared_ptr<student> Tom(nullptr); will work.
Shared_ptr<student> Tom(42); will not work, because Shared_ptr doesn't have the right constructor, but it has nothing to do with getting the line number.

How do I create an array or list of externally managed objects in modern c++?

I am building an add-in for a program. My add-in manipulates Ptr objects passed to me by the host application. I would like to create a vector of externally created and managed objects by the host. Unfortunately, the documentation doesn't have any clear examples of how to do this.
class Players {
vector<Ptr<Player>> vectorOfGamers; // who deletes and when this?
public void CreatePlayers () {
// call static application to create three players
for ( int i = 0; i < 3; i++ )
vectorOfGamers.push_back(Application.GetNextPlayer());
}
}
Confused about how to build this class and prevent memory leaks and causing a null exception if items are deleted prematurely. Also, how do I use modern C++ facilities to achieve this yet gain as much of the benefits of the new memory management like make_shared, make_unique, nullptr, etc?
For your information, below is a snapshot of Ptr.I am confused about the Ptr as it appears superfluous given modern C++'s new memory management facilities.
class IncompleteType
{
public:
template<typename T> static void addref(void* ptr) { reinterpret_cast<adsk::core::ReferenceCounted*>(ptr)->addref(); }
template<typename T> static void release(void* ptr) { reinterpret_cast<adsk::core::ReferenceCounted*>(ptr)->release(); }
};
class CompleteType
{
public:
template<typename T> static void addref(T* ptr) { ptr->addref(); }
template<typename T> static void release(T* ptr) { ptr->release(); }
};
template<class T, class PT = IncompleteType>
class Ptr
{
public:
typedef T element_type;
Ptr() : ptr_(nullptr) {}
Ptr(const Ptr& rhs) : ptr_(nullptr) { reset(rhs.ptr_); }
Ptr(const T* ptr, bool attach = true) : ptr_(nullptr) { reset(ptr, attach); }
// casting constructor. call operator bool to verify if cast was successful
template<class V, class VPT>
Ptr(const Ptr<V, VPT>& rhs) : ptr_(nullptr) {
if (rhs)
reset(rhs->template query<T>(), false);
}
~Ptr() { reset(nullptr); }
void operator=(const Ptr<T, PT>& rhs) { if (&rhs != this) reset(rhs.ptr_); }
void operator=(const T* ptr) { reset(ptr, true); }
// casting assignment operator. call operator bool to verify if cast was successful
template<class V, class VPT>
void operator=(const Ptr<V, VPT>& rhs) {
if (rhs)
reset(rhs->template query<T>(), false);
else
reset(nullptr);
}
void reset(const T* ptr, bool attach = false) {
if (ptr_ != ptr)
{
if (ptr_)
PT::template release<T>(ptr_);
ptr_ = const_cast<T*>(ptr);
if (!attach && ptr_)
PT::template addref<T>(ptr_);
}
}
T* operator->() const { assert(ptr_ != nullptr); if (ptr_ == nullptr) throw std::exception(); return ptr_; }
// Test if this pointer is empty (if operator-> will throw)
/*explicit*/ operator bool() const { return ptr_ != nullptr; }
bool operator==(const Ptr& rhs) const { return ptr_ == rhs.ptr_; }
bool operator!=(const Ptr& rhs) const { return ptr_ != rhs.ptr_; }
bool operator<(const Ptr& rhs) const { return ptr_ < rhs.ptr_; }
// Iteration support. Only usable if T has count and item members and an iterable_type
typedef Iterator<T, PT> iterator;
iterator begin() const { return Iterator<T, PT>(*this); }
iterator end() const { return Iterator<T, PT>(*this, true); }
// Caution the following functions if used incorrectly can cause a reference count leak
T* get() const { return ptr_; }
T* detach() { T* t = ptr_; ptr_ = nullptr; return t; }
private:
T* ptr_;
};
You’re probably right that Ptr wouldn’t be needed in a post-C++03 environment. Probably your plugin API is old enough that C++11 wasn’t around back then. From the code you posted my best guess is that Ptr is supposed to be a reference counted smart pointer that manages shared ownership like std::shared_ptr does.
How exactly you use that thing should become clear from the plguin API documentation and maybe the source code of your host program. Just from the snippet you posted and without even mentioning the program’s name it’s impossible to say anything definite.

Management of a vector of pointers as a class member

I am in a rather specific situation that requires me to use a vector of raw pointers as a class member:
I need to keep a list of abstract objects (I understood I could not use vector<AbstractClass> directly).
I can't use Boost or std::tr1, so no smart pointer library (because of constraints from the hardware I'm using).
Here is a basic example of what I need:
#include <vector>
class Foo
{
virtual void foo() {}
};
class Bar : public Foo
{
void foo(){}
};
class FooBar
{
vector<Foo*> list;
void add(){ list.push_back(new Bar()); }
};
Coming from Java, I am very scared of pointers and memory leaks. This post initially suggests to delete the objects manually when the object goes out of scope. Is it enough to do this in the destructor of FooBar?
class FooBar
{
vector<Foo*> list;
void add(){ list.push_back(new Bar()); }
~FooBar(){
for(vector<Foo*>::iterator it=list.begin(); it!=list.end(); it++)
delete *it;
}
};
By the rule of three, I guess I also have to implement a copy constructor and an assignment operator. Is the following (based on this and that posts) a correct implementation ?
FooBar::FooBar(const FooBar& orig) : list(orig.list.size()) {
try {
vector<Foo*>::iterator thisit = list.begin();
vector<Foo*>::const_iterator thatit = orig.list.cbegin();
for (; thatit != orig.list.cend(); ++thisit, ++thatit)
*thisit = *thatit; // I'm okay with a shallow copy
} catch (...) {
for (vector<Foo*>::iterator i = list.begin(); i != list.end(); ++i)
if (!*i)
break;
else
delete *i;
throw;
}
}
FooBar& operator=(const FooBar& orig){
FooBar tmp(orig);
swap(tmp);
return *this;
}
Your code has some major flaws as far as I can tell. First of all, your first code is correct, and yes, your destructor does what it should do, as long as your class owns the objects and doesn't get copied.
Also, your copy constructor and assignment operator looks problematic. First of all, if you are okay with shallow copying, you don't even have to write the functions anyway, std::vector's copy constructor and assignment operators do what you have done manually anyway. I don't think you need that try-catch block. By the way, where is the implementation for that swap in your assignment operator?
For your purposes, a very small reference counting scheme would work:
class FooPtr
{
Foo* _raw;
size_t* _ctl;
FooPtr(Foo* ptr) :
_raw(ptr), _ctl(new size_t(0))
{
}
template <class T>
FooPtr(T* ptr) : FooPtr(static_cast<Foo*>(ptr)) {}
FooPtr(const FooPtr& rhs) :
_raw(rhs._raw), _ctl(rhs._ctl)
{
++(*_ctl);
}
~FooPtr()
{
if (_raw == nullptr) return;
--(*_ctl);
if (*_ctl == 0)
{
delete _raw;
delete _ctl;
}
}
FooPtr& operator=(FooPtr ptr)
{
std::swap(*this, ptr);
return *this;
}
Foo* operator->()
{
return _raw;
}
Foo& operator*()
{
return *_raw;
}
}
Your class now looks like this, no destructor, copy ctor or assignment operator needed:
class FooBar
{
vector<FooPtr> list;
void add(){ list.emplace_back(new Bar()); }
}
I've written this quickly, it might contain bugs though it gives you the basic idea I suppose. Also, I've used some c++11 features like emplace_back and nullptr but they can easily be converted in non 11 code.
I would write a small wrapper around a vector that frees the elements for you:
template<class Container>
void delete_elements(Container& cont) {
typedef typename Container::reverse_iterator iterator;
iterator end = container.rend();
for (iterator i = container.rbegin(); end != i; ++i) {
delete *i;
}
}
struct deep_copy {
template<typename T>
T* operator()(T const* const other) {
return other->clone();
}
}
template<typename T>
struct ptr_vector {
std::vector<T*> container;
ptr_vector() { }
ptr_vector(ptr_vector const& other) {
std::vector<T*> tmp;
tmp.reserve(other.container.size());
try {
std::transform(other.container.begin(), other.container.end(),
std::back_inserter(tmp), deep_copy());
}
catch (...) {
(delete_elements)(tmp);
throw;
}
container.swap(tmp);
}
ptr_vector& operator=(ptr_vector other) {
container.swap(other.container);
return *this;
}
~ptr_vector() {
(delete_elements)(container);
}
};
Now you can just use a ptr_vector<Foo> v and use v.container to add elements. Note: you should add a clone member function to your base class so you don't slice when you want to copy.

invalid initialization of non-const reference of type from an rvalue of type

I am writing some code based on issue 28 smart pointer of more effective c++ as follows. However, it cannot compile:
main.cpp: In instantiation of 'SmartPointer<T>::operator SmartPointer<U>() [with U = MusicProduct; T = Cassette]':
main.cpp:99:17: required from here
main.cpp:48:39: error: invalid initialization of non-const reference of type 'SmartPointer<MusicProduct>&' from an rvalue of type 'SmartPointer<MusicProduct>'
return SmartPointer<U> (ptr_);
^
main.cpp:16:9: note: initializing argument 1 of 'SmartPointer<T>::SmartPointer(SmartPointer<T>&) [with T = MusicProduct]'
SmartPointer(SmartPointer<T>& other)
^
Either of these two changes works:
in the implementation of operator SmartPointer (), create an object and return:
SmartPointer a(ptr_);
return a;
Or, make the parameter of the copy constructor as const
SmartPointer(const SmartPointer& other)
and comment the line
other.ptr_ = nullptr;
Is there any reason why either of the solutions works? Thanks.
#include <iostream>
template <typename T>
class SmartPointer
{
public:
SmartPointer(T* ptr) : ptr_(ptr) {}
~SmartPointer()
{
if (ptr_)
{
delete ptr_;
}
}
SmartPointer(SmartPointer<T>& other)
{
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
SmartPointer<T>& operator = (SmartPointer<T>& other)
{
if (this == &other)
{
return *this;
}
if (ptr_)
{
delete ptr_;
}
ptr_ = other.ptr_;
other.ptr_ = nullptr;
return *this;
}
template <typename U>
operator SmartPointer<U> ()
{
// it works
//SmartPointer<U> a(ptr_);
//return a;
// error
return SmartPointer<U> (ptr_);
}
T& operator * () const
{
return *ptr_;
}
T* operator -> () const
{
return ptr_;
}
private:
T* ptr_ = nullptr;
};
class MusicProduct
{
public:
MusicProduct(const std::string& name) : name_(name) {}
virtual ~MusicProduct() {}
virtual void Play() const = 0;
virtual void ShowName() const
{
std::cout << name_ << std::endl;
}
private:
std::string name_;
};
class Cassette : public MusicProduct
{
public:
Cassette(const std::string& name) : MusicProduct(name) {}
void Play () const
{
std::cout << "play cassette" << std::endl;
}
};
void CallPlay(const SmartPointer<MusicProduct>& sp)
{
sp->Play();
}
int main()
{
SmartPointer<Cassette> a(new Cassette("Zhang"));
a->ShowName();
CallPlay(a);
return 0;
}
That's because your copy ctor has a non-const reference parameter and therefore cannot accept a temporary. Thus
return SmartPointer<X>(y);
won't work. The argument to the return keyword is a temporary, and it needs to be copied, so here the design breaks down.
Since you are using C++11, you can fix this by introducing a move constructor (and move assignment).
You can also make the argument const and designate the ptr_ member as mutable. This will allow you to copy from temporaries and const smart pointers, but for the price of actually mutating them.

Segmentation fault in destructor of own shared pointer

For training purposes, I am trying to write my own smartpointer, imitating std::shared_ptr. I have a static std::map<void *, int> ref_track that keeps track whether there are still shared pointer referencing a certain block in memory.
My concept is this:
template <typename PType>
class shared_ptr
{
public:
shared_ptr()
: value_(nullptr), ptr_(nullptr)
{}
template <typename T>
explicit shared_ptr(T * ptr)
: shared_ptr()
{
reset(ptr);
}
template <typename T>
shared_ptr(shared_ptr<T> const & other)
: shared_ptr()
{
reset(other.get());
}
~shared_ptr()
{
reset();
}
void reset()
{
if(value_)
{
delete value_; // Segmentation fault here!
value_ = 0;
ptr_ = 0;
}
}
template <typename T>
void reset(T * ptr)
{
reset();
if(ptr)
{
value_ = new shared_ptr_internal::storage_impl<
T
>(ptr);
ptr_ = ptr;
}
}
PType * get() const
{
return ptr_;
}
typename shared_ptr_internal::ptr_trait<PType>::type operator *()
{
return *ptr_;
}
private:
shared_ptr_internal::storage_base * value_;
PType * ptr_;
};
When running my test suite, I noticed that
shared_ptr<int> a(new int(42));
a.reset(new int(13));
works fine, but
shared_ptr<int> a(new int(42));
a = shared_ptr<int>(new int(13));
leads to problems: *a is 0 instead of 13, and delete value_ crashes with a segmentation fault in the destructor of a. I have marked the crash in the source code with a comment.
The used internal classes are
namespace shared_ptr_internal
{
typedef std::map<void *, int> ref_tracker;
typedef std::map<void *, int>::iterator ref_tracker_iterator;
typedef std::pair<void *, int> ref_tracker_entry;
static ref_tracker ref_track;
struct storage_base
{
virtual ~storage_base() {}
};
template <typename PType>
struct storage_impl : storage_base
{
storage_impl(PType * ptr)
: ptr_(ptr)
{
ref_tracker_iterator pos = ref_track.find(ptr);
if(pos == ref_track.end())
{
ref_track.insert(
ref_tracker_entry(ptr, 1)
);
}
else
{
++pos->second;
}
}
~storage_impl()
{
ref_tracker_iterator pos = ref_track.find(ptr_);
if(pos->second == 1)
{
ref_track.erase(pos);
delete ptr_;
}
else
{
--pos->second;
}
}
private:
PType * ptr_;
};
template <typename PType>
struct ptr_trait
{
typedef PType & type;
};
template <>
struct ptr_trait<void>
{
typedef void type;
};
}
Sorry for the bulk of source code, but I really do not know how I could narrow it down further. I would be grateful for any ideas what could be causing the segfault, and moreover why this does not happen when using reset manually.
Update
My (not-working) assignment operator:
template <typename T>
shared_ptr<PType> & operator =(shared_ptr<T> const & other)
{
if(this != &other)
{
value_ = nullptr;
ptr_ = nullptr;
reset(other.get());
}
return *this;
}
You're missing an assignment operator.
This means that in the following code:
a = shared_ptr<int>(new int(13));
a temporary shared pointer is created; then the default assignment operator simply copies the pointer to a without releasing the old value or updating the reference count; then the temporary deletes the value, leaving a with a dangling pointer.
Seems like a violation of the rule of three: You have a custom copy constructor and a custom destructor, but no custom assignment operator. Therefore a = shared_ptr<int>(new int(13)) will just copy the two pointers value_ and ptr_ from the temporary, without any update of your reference tracking. Therefore when you destroy the temporary, there are no more tracked references to that pointer, which will lead to its deletion. Also note that the old pointer will have been leaked in the assignment.
you forgot to add an assignment operator to your pointer class that should decrement the number references to the old object and increment the number of references to the assigned object. Most times it's the easiest way to implement operator= in terms of a copy d'tor and a swap function:
shared_ptr& shared_ptr<T>::operator=( shared_ptr<T> other )
{
other.swap( this );
return *this;
}