about this smart pointer class - c++

I run into this code in a book, a simple smart pointer, have a few questions about it:
template <class T>
class SmartPointer {
public:
SmartPointer(T * ptr) {
ref = ptr;
ref_count = malloc(sizeof(unsigned));
*ref_count = 1;
}
SmartPointer(SmartPointer<T> & sptr) {
ref = sptr.ref;
ref_count = sptr.ref_count;
++*ref_count;
}
SmartPointer<T> & operator=(SmartPointer<T> & sptr) {
if (this != &sptr) {
ref = sptr.ref;
ref_count = sptr.ref_count;
++*ref_count;
}
return *this;
}
~SmartPointer() {
--*ref_count;
if (*ref_count == 0) {
delete ref;
free(ref_count);
ref = ref_count = NULL;
}
}
T* operator->() { return ref; }
T& operator*() { return *ref; }
protected:
T * ref;
unsigned * ref_count;
};
here are my questions:
1. why is the ref_count initialized by using malloc? why can't it be ref_count = new unsigned();
2. the = operator function, doesn't it need to clean up the old value? this code seems to cause a ref counting error.
Thanks,

This book you have is rubbish. You need to throw it away. This code doesn't even compile.
malloc is not any better than new unsigned(1). malloc() returns void* which needs to be cast to unsigned* which is obviously not done here.
You are right, the reference count of the object previously pointed to needs to be taken care of.
Why not try looking into the implementation of boost::shared_ptr or std::shared_ptr? It's very likely that you'll end up using it anyway.

why is the ref_count initialized by using malloc?
It can use std::new as well. The primary difference between new and malloc is that new provides an opportunity to suitably initialize your object by calling its constructor in addition to allocating dynamic memory.In case of in-built data type like unsigned int(as in your example code) essentially this important difference doesn't matter.
Note that usually std::new implementations also essentially call a malloc for memory allocation.
The = operator function, doesn't it need to clean up the old value? this code seems to cause a ref counting error
Indeed there is an error.
The = operator in this implementation checks if the pointer is being assigned to itself and if yes it correctly increases the reference count by 1, Since there is one more entity which shares this pointer after the = is called.
If the scenario is not self assignment then the implementation should reduce the reference count of the pointer being assigned to by 1, check if the count is 0 and if it is zero, deallocate the same.

This is bad code. Self-assignment is non-idiomatic, awful use of malloc, buggy reference counting implementation, no const on the copy assignment operator/constructor argument, and doesn't deal with SmartPointers that point to NULL. Throw your book away and get one that works.

Related

Is there any advantage to moving a pointer?

I'm working with a code base right now, and there's this section that has the following:
if(const auto *temp_ptr = obj->get_ptr()) {
perm_ptr = std::move(temp_ptr);
}
What is the point of the std::move in this case when it's just a simple pointer? It doesn't seem there's any advantage over simply doing perm_ptr = temp_ptr?
As said in the comments, if the type of perm_ptr is a raw pointer type, then the move is pointless.
However, it is possible to define a type for perm_ptr that would make this syntax meaningful. Whether it would be useful is at best highly debatable, and results in unintuitive and unconventional code.
It involves defining an assignment operator that only accepts a RValue to a pointer. Like so: operator=(T*&& p);.
This could, for instance, be used to ensure that no one accidentally assigns a raw pointer to something that will eventually calling delete on it without doing so intentionally.
It could also be used to reset the pointer to nullptr (or some other value) as part of the assignment.
#include <utility>
template<typename T>
struct my_unique_ptr {
~my_unique_ptr() {
if(ptr_) delete ptr_;
}
my_unique_ptr& operator=(T*&& p) {
ptr_ = p;
p = nullptr;
return *this;
}
private:
T* ptr_ = nullptr;
};
int main() {
my_unique_ptr<int> perm_ptr ;
int* ptr = new int;
// all good.
owning_ptr = std::move(ptr);
// compile error!
owning_ptr = ptr;
}
To be 100% clear: This is a only an example that could result in the program presented by the OP being intentional and meaningful. I am not advocating for such a technique. Even if it was a good idea, it would still be fraught with caveats and pitfalls that are not being covered by this answer.

How to free memory with move constructor

I am trying to create my own vector and here is a minimal example to introduce the problem I have:
class DemoVector {
public:
DemoVector() : capacity_(1), size_(0) {
data_ = new int[1];
}
DemoVector(DemoVector&& rhs) {
data_ = std::move(rhs.data_);
size_ = rhs.size_;
capacity_ = rhs.capacity_;
}
~DemoVector() {
delete[] data_;
}
void PushBack(const int &v) {
// doesn't matter
}
private:
int *data_;
size_t capacity_;
size_t size_;
};
Test:
TEST_CASE("Test") {
DemoVector b;
b.PushBack(1);
DemoVector c(std::move(b));
}
I have a problem here and I understand why. I have two objects which points on the same memory. Second destructor tries to free memory, which have already been freed by first destructor.
But I don't know how to fix it.
Thank you for your help.
std::move(rhs.data_) doesn't actually move anything. std::move is nothing more than a named cast. It produces an rvalue reference that allows move semantics to occur. But for primitive types, it's just a copy operation. The pointer is being copied, and so you end up with two pointers that contain the same address. Since you don't want the source object to still be pointing at the the same buffer, simply modify it. That's why move-semantics is build around non-const references.
Move constructors are commonplace now, so there's a standard utility (C++14) to help write them in a way that makes code behave more as you'd expect. It's std::exchange. You can simply write
DemoVector(DemoVector&& rhs)
: data_(std::exchange(rhs.data_, nullptr))
, size_(std::exchange(rhs.size_ , 0))
, capacity_(std::exchange(rhs.capacity_ , 0))
{}
And all the values get adjusted properly. std::exchange modifies its first argument to hold the value of the second argument. And finally, it return the old value of the first argument. Very handy to shift values around in one-liner initializations.
Because std::move is basically just a cast that doesn't actually move anything! You need to update the values in the other object yourself:
DemoVector(DemoVector&& rhs) {
data_ = rhs.data_;
size_ = rhs.size_;
capacity_ = rhs.capacity_;
rhs.data_ = nullptr;
rhs.size_ = 0;
rhs.capacity = 0;
}
Or alternatively to make use of the existing constructor:
DemoVector(DemoVector&& rhs): DemoVector() {
// Or write your own swap function to reuse this elsewhere
std::swap(data_, rhs.data_);
std::swap(size_, rhs.size_);
std::swap(capacity_, rhs.capacity_);
}
It's up to you how you want users of your class to handle moved-from objects. In the second case, and possibly also the first depending on how the rest of your class works, rhs (b in your test case) will be an empty vector.

How to use shared_ptr to manage already ref-count managed objects?

if an object is already reference-counted (like glib in C), having obj_ref, obj_unref. All we have is a pointer like obj *p.
How can we use c++'s shared_ptr to manage the object so that we can have an uniform interface.
Ok, it seems that a lot of people have misunderstood my intension.
The greatest issue here is not about deleter. It's about inform of the original manager that I increased the refcount.
If I assigned or copied, only std::shared_ptr increased the refcount, but the original one did not. Is there anyway to inform it? So as the unref operation.
std::shared_ptr allows you to pass a custom deleter which is called when the owned object should be destroyed. You could use it to call obj_unref.
obj* p = create_obj();
p->obj_ref();
std::shared_ptr<obj> sp(p, [](auto p) {
p->obj_unref();
});
/* use sp normally, obj will be 'obj_unref'ed and deleted when sp goes out of scope */
I don't know how a obj is created and if it gets destroyed by obj_unref() when the count reaches 0, but I hope you see what I mean.
The idea is to increment objs internal reference count just once at the beginning, and decrement it just once when the last shared_ptr is destroyed.
Don't try to somehow duct tape std::shared_ptr's refcounting to your custom one, that won't end well. Just write a custom pointer:
struct objPtr {
objPtr()
: _ptr{nullptr} { }
objPtr(obj *ptr)
: _ptr{ptr} {
if(_ptr)
_ptr->obj_ref();
}
~objPtr() {
if(_ptr)
_ptr->obj_unref();
}
objPtr(objPtr const &orig)
: objPtr{orig._ptr} { }
objPtr &operator = (objPtr const &orig) {
obj *const oPtr = std::exchange(_ptr, orig._ptr);
_ptr->obj_ref();
oPtr->obj_unref();
return *this;
}
obj &operator * () { return *_ptr; }
obj const &operator * () const { return *_ptr; }
obj *operator -> () { return _ptr; }
obj const *operator -> () const { return _ptr; }
operator bool() const { return _ptr; }
bool operator ! () const { return !_ptr; }
private:
obj *_ptr;
};
Add move construction and assignment if you so wish.
When you want a shared_ptr, start with a unique_ptr. Then build up.
struct cleanup_obj {
// not called with nullptr:
void operator()(obj* t)const {
obj_unref(t);
}
};
using obj_unique_ptr = std::unique_ptr<T, cleanup_obj>;
using obj_shared_ptr = std::shared_ptr<T>;
template<class T>
obj_unique_ptr<T> make_unique_refcount( T* t ) {
using ptr=obj_unique_ptr<T>;
if (!t) return ptr();
obj_ref(t);
return ptr(t);
}
template<class T>
obj_shared_ptr<T> make_shared_refcount( T* t ) {
return make_unique_refcount(t); // implicit convert does right thing
}
What did I do?
First, I wrote a unique_ptr wrapper, because we may as well be complete, and it solves the shared_ptr case via the unique_ptr->shared_ptr implicit conversion.
For unique_ptr, we have to say we aren't using the default object destroyer. In this case, we are using a stateless function object that knows how to obj_unref an obj*. The stateless function object keeps the overhead at zero.
For the null case, we don't first add a reference, as that is rude.
For shared_ptr, the fact that we have a working unique_ptr makes it a free function. shared_ptr will happily store the destroyer function that unique_ptr has. It doesn't have to be told it has a special object destroyer, because shared_ptr type erases object destruction by default. (This is because unique_ptr<T> is zero-overhead over a naked pointer, while shared_ptr<T> has unavoidable overhead of the reference counting block; the designers figured once you have that reference counting block, adding in a type-erased destruction function was not really expensive).
Note that our obj_unique_ptr<T> is also zero overhead over a naked pointer. Quite often you'll want one of these instead of the shared one.
Now, you can upgrade the obj_unique_ptr to a full on intrusive pointer, with less overhead than a shared_ptr, if you want.
template<class T>
struct obj_refcount_ptr : obj_unique_ptr<T> // public
{
// from unique ptr:
obj_refcount_ptr(obj_unique_ptr<T> p):obj_unique_ptr<T>(std::move(p)){}
obj_refcount_ptr& operator=(obj_unique_ptr<T> p){
static_cast<obj_unique_ptr<T>&>(*this)=std::move(p);
return *this;
}
obj_refcount_ptr(obj_refcount_ptr&&)=default;
obj_refcount_ptr& operator=(obj_refcount_ptr&&)=default;
obj_refcount_ptr()=default;
obj_refcount_ptr(obj_refcount_ptr const& o):
obj_refcount_ptr(make_unique_refcount(o.get())
{}
obj_refcount_ptr& operator=(obj_refcount_ptr const& o) {
*this = make_unique_refcount(o.get());
return *this;
}
};
which I think covers it. Now it is a zero-overhead reference counting intrusive smart pointer. These intrusive smart pointers can be converted toa std::shared_ptr<T> via implicit conversion, as they are still unique_ptrs. They are just unique_ptrs we have taught to copy themselves!
It does require moving from an obj_refcount_ptr to get a shared_ptr. We can fix this:
operator std::shared_ptr<T>() const {
return obj_refcount_ptr(*this);
}
which creatres an obj_refcount_ptr copy of *this and moves it into the shared_ptr. Only one add ref is called, and the remove ref is only called when the shared_ptr count goes to zero.
The general approach is to start with the simplest smart pointer (unique_ptr), get it right, then exploit its implementation to get us the shared_ptr and eventually the refcount_ptr. We can test the unique_ptr implementation in isolation, and its correctness makes testing the richer pointers easier.
The most simplest approach, the least invasive one with the minimal possibility of breaking something, is to simply write your own facade for the object, with the underlying object as a private member and providing simple wrappers to access it.
Then use a std::shared_ptr to that.
It's an incredibly bad idea to have the same objects in multiple smart pointer implementations as their ref counts can't know about each other. As soon as the ref count hits zero in one it will delete the object even if the other still holds refs.
If you really had to you could construct your smart pointers with custom deleters (that do nothing), but I really wouldn't recommend this approach.
Pick one implementation and stick to it.

A simple implementation of Smart Pointer Class

In book C++ Primer 13.5.1, it implement a Smart Pointer Class using a Use-Count Class. Their implementation is as follows:
Use-Count Class
// private class for use by HasPtr only
class U_Ptr {
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p): ip(p), use(1) { }
~U_Ptr() { delete ip; }
};
Smart Pointer Class
/*
smart pointer class: takes ownership of the dynamically allocated
object to which it is bound
User code must dynamically allocate an object to initialize a HasPtr
and must not delete that object; the HasPtr class will delete it
*/
class HasPtr {
public:
// HasPtr owns the pointer; p must have been dynamically allocated
HasPtr(int *p, int i)
: ptr(new U_Ptr(p)), val(i) { }
// copy members and increment the use count
HasPtr(const HasPtr &orig)
: ptr(orig.ptr), val(orig.val) { ++ptr->use; }
HasPtr& operator=(const HasPtr&);
// if use count goes to zero, delete the U_Ptr object
~HasPtr() { if (--ptr->use == 0) delete ptr; }
friend ostream& operator<<(ostream&, const HasPtr&);
// copy control and constructors as before
// accessors must change to fetch value from U_Ptr object
int *get_ptr() const { return ptr->ip; }
int get_int() const { return val; }
// change the appropriate data member
void set_ptr(int *p) { ptr->ip = p; }
void set_int(int i) { val = i; }
// return or change the value pointed to, so ok for const objects
// Note: *ptr->ip is equivalent to *(ptr->ip)
int get_ptr_val() const { return *ptr->ip; }
void set_ptr_val(int i) { *ptr->ip = i; }
private:
U_Ptr *ptr; // points to use-counted U_Ptr class
int val;
};
Wonder: I am curious about why not simply using a int * to act like the Use-Count Class, just like the int* countPtr; used in the following new Smart Pointer Class:
class T
{
private:
int* countPtr; //
int* p;
int val;
public:
T(){
p = new int();
countPtr = new int();
*countPtr = 1;
val = 0;
}
T(T& t){
p = t.p;
countPtr = t.countPtr;
val = t.val;
*countPtr += 1;
}
T& operator = ( const T& rT){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
p = rT.p;
countPtr = rT.countPtr;
val = rT.val;
*countPtr += 1;
return *this;
}
~T(){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
}
int *get_ptr() const { return p; }
int get_int() const { return val; }
// change the appropriate data member
void set_ptr(int *ptr) { p = ptr; }
void set_int(int i) { val = i; }
};
Test: I tested the above Smart Pointer Class using code like the following and it seems working well.
int main()
{
T t1;
T t2(t1);
T t3(t1);
T t4;
t4 = t1;
return 0;
}
Real question: Is this new Smart Pointer Class with simply a int *countPtr sufficient enough? If yes, why bother to use an extra Use-Count Class like in the book? If no, what do I miss?
One property of the original implementation is that the delete is performed, in the control block object, with the original pointer type. This is a partial type erasure. No matter how much the smart pointer objects are copied, with somewhat different types, the original control block remains the same, with delete via the original pointer type.
However, since the original code you show is not templated, one must assume that it is an early example, followed later by similar templated code.
Converting a pointer up in a base class hierarchy, as can happen with copying of a smart pointer, means that delete on the new pointer type is only valid if the statically known new type has a virtual destructor.
For example, std::shared_ptr also deletes (guaranteed) via the original pointer type, unless one explicitly supplies a deleter functor that does something else.
My guess is that the author - whether consciously or subconsciously - is aware that having a separate class is useful in real-world smart pointers, e.g.:
a count of weak pointers (not sure if you'll have heard of them yet - they track an object without extending its lifetime, such that you can try to convert one into a (normal) shared pointer later, but it only works if there's at least one shared pointer to the object around to have kept it alive)
a mutex to make the shared pointer thread safe (though atomic operations may be better when available),
debugging information (e.g. boost::shared_ptr has a #ifdef to include an shared counter id)
virtual dispatch table, used by e.g. boost shared pointers to dispatch to OS-appropriate code (see boost/smart_ptr/detail/sp_counted_base_*.hpp headers)
I don't know the book, but perhaps they'll go on to explain what else might go into U_Ptr....
Your code is equivalent to the standard code reported by the book. However it is worst in some respects:
you need two allocations/deallocations instead of one (two ints instead of a single object). This might be slower and a little bit more difficult to manage.
you have a copy of the pointer duplicated in every object. So: duplicated information which you should guarantee to keep valid.
your object is larger (two pointers instead of one)
You only have one positive note:
the access to the pointer is direct instead of having one indirection. This could mean that the access to the referred object is slightly faster with your implementation...
(Updated 14.4.2017)
I by myself tried out unique_ptr and shared_ptr and was bit surprised to see that those classes does not make your life easier. I had function in one API where function would pick up Object*& - fill it out (pointer) and after that you need to delete that object. It's possible to use c++11, but you need to add extra temporary pointer for that purpose. (So use of *_ptr classes does not makes my life easier)
How complex would it be to implement smart pointer ?
Just by quickly glancing auto_ptr class implementation, I've quickly coded simple smart point container class, but then I've noticed that I need to support multiple smart pointers referencing the same object pointer.
Ok, so how complex could it be to code reference counting - I through, and went to google - and found one interesting article about it:
http://www.codingwisdom.com/codingwisdom/2012/09/reference-counted-smart-pointers-are-for-retards.html
Somehow I tend to agree with author of that article and comments written in that article that reference counting makes life even more complex, but still trying to stick to plain C also sounds bit dumb.
I'll now add code snippet here of my own class, and if you want to get newest version, you can check in this svn repository: https://sourceforge.net/p/testcppreflect/code/HEAD/tree/SmartPtr.h
Below is older version.
#pragma once
//
// If you're using multithreading, please make sure that two threads are not accessing
// SmartPtr<> pointers which are cross linked.
//
template <class T>
class SmartPtr
{
public:
SmartPtr() : ptr( nullptr ), next( nullptr )
{
}
SmartPtr( T* pt ) : ptr( pt ), next( nullptr )
{
}
SmartPtr( SmartPtr<T>& sp ) : ptr( nullptr ), next( nullptr )
{
operator=(sp);
}
~SmartPtr()
{
release();
}
// Reference to pointer - assumed to be filled out by user.
T*& refptr()
{
release();
return ptr;
}
// Pointer itself, assumed to be used.
T* get()
{
return ptr;
}
T* operator->() const
{
return ptr;
}
T* operator=( T* _ptr )
{
release();
ptr = _ptr;
return ptr;
}
SmartPtr<T>& operator=( SmartPtr<T>& sp )
{
release();
ptr = sp.ptr;
if ( ptr ) // If we have valid pointer, share ownership.
{
if( sp.next == nullptr )
{
next = &sp;
sp.next = this;
} else {
SmartPtr<T>* it = &sp;
while( it->next != &sp )
it = it->next;
next = &sp;
it->next = this;
}
}
return *this;
}
void release()
{
if ( !ptr )
return;
// Shared ownership.
if( next != nullptr )
{
// Remove myself from shared pointer list.
SmartPtr<T>* it = next;
while( it->next != this )
it = it->next;
if( it == it->next->next )
it->next = nullptr;
else
it->next = next;
next = nullptr;
ptr = nullptr;
return;
}
// Single user.
delete ptr;
ptr = nullptr;
}
T* ptr; // pointer to object
SmartPtr<T>* next; // nullptr if pointer is not shared with anyone,
// otherwise cyclic linked list of all SmartPtr referencing that pointer.
};
I've replaced reference counting with simple linked list - linked list keeps all class instances referenced, each destructor removes one reference away.
I've decided to rename operator* to refptr() function just to avoid developers writing extra fancy code. ("C++ jewels")
So in general I agree with article above - please don't make smart pointers too smart. :-)
I'm free to any improvement suggestions concerning this class and potential bugfixes.
And I wanted also to answer to original author's questions:
Real question: Is this new Smart Pointer Class with simply a int *countPtr
sufficient enough? If yes, why bother to use an extra Use-Count Class
like in the book? If no, what do I miss?
You're using separate mechanics for count management, like article link above mentions - it will becomes non-trivial to follow and debug reference counting. In my code snippet I use linked list of smart pointer instances, which does not perform any allocation ( so implementation above is faster than any other existing smart pointer implementation ), also it easier debugging of smart pointer itself - you can check by link (next) who locks down your memory from being collected.
But in overall - if you are experiencing memory leaks, I would say that it's highly non-trivial to find where they are if you're not originally made that code. Smart class pointer does not help in that sense to figure out who and how much has leaked out. Better to code once and properly that to fight with your own beast later on.
For memory leaks I recommend to find existing tools and use them - for example this one:
https://sourceforge.net/projects/diagnostic/
(There are plenty of them, but none of them works reliably/good enough).
I know that you're eager to put dislike to this implementation, but really - please tell me what obstacles you see in this implementation ?!

Implementing Smart Pointer - Dynamic Allocation with templates

I'm in the process of writing a smart pointer countedptr and I've hit a speed bump. The basic function of countedptr is to work like any other smart pointer and also have a count of how many pointers are pointing to a single object. So far, the code is:
[SOLVED]
#include "std_lib_facilities.h"
template <class T>
class counted_ptr{
private:
T* pointer;
int* count;
public:
counted_ptr(T* p = 0, int* c = new int(1)) : pointer(p), count(c) {} // default constructor
explicit counted_ptr(const counted_ptr& p) : pointer(p.pointer), count(p.count) { ++*count; } // copy constructor
~counted_ptr() { --*count; delete pointer; }
counted_ptr& operator=(const counted_ptr& p)
{
pointer = p.pointer;
count = p.count;
++*count;
return *this;
}
T* operator->() const{ return pointer; }
T& operator*() const { return *pointer; }
int Get_count() const { return *count; }
};
int main()
{
counted_ptr<double> one;
counted_ptr<double>two(one);
int a = one.Get_count();
cout << a << endl;
}
When I try to do something like
one->pointer = new double(5);
then I get a compiler error saying "request for member 'pointer' in '*(&one)->counted_ptr::operator->with T = double' which is of non-class type double".
I considered making a function to do this, and while I could make a function to allocate an array of T's, I can't think of a way of making one for allocating actual objects. Any help is appreciated, thanks.
Old Solution
What about another assignment operator?
counted_ptr& counted_ptr::operator=(T* p)
{
if (! --*count) { delete count; }
pointer = p;
count = new int(1);
return *this;
}
...
one = new double(5);
Also, your destructor always deletes a shared pointer, which is probably what caused *one to be a random nomber. Perhaps you want something like:
counted_ptr::~counted_ptr() { if (! --*count) { delete pointer; delete count; } }
New Solution
As you want repointing a counted_ptr (eg one = new double(5)) to update all related counted_ptrs, place both the pointer and the count in a helper class, and have your pointer class hold a pointer to the helper class (you might already be headed down this path). You could go two ways in filling out this design:
Make the helper class a simple struct (and a private inner class) and place all the logic in the outer class methods
Make counted_ptr the helper class. counted_ptr maintains a reference count but doesn't automatically update the count; it's not a smart pointer, it only responds to release and retain messages. If you're at all familiar with Objective-C, this is basically its traditional memory management (autoreleasing aside). counted_ptr may or may not delete itself when the reference count reaches 0 (another potential difference from Obj-C). counted_ptrs shouldn't be copyable. The intent is that for any plain pointer, there should be at most one counted_ptr.
Create a smart_ptr class that has a pointer to a counted_ptr, which is shared among smart_ptr instances that are supposed to hold the same plain pointer. smart_ptr is responsible for automatically updating the count by sending its counted_ptr release and retain methods.
counted_ptr may or may not be a private inner class of shared_ptr.
Here's an interface for option two. Since you're doing this as an exercise, I'll let you fill out the method definitions. Potential implementations would be similar to what's already been posted except that you don't need a copy constructor and copy assignment operator for counted_ptr, counted_ptr::~counted_ptr doesn't call counted_ptr::release (that's smart_ptr::~smart_ptr's job) and counted_ptr::release might not free counted_ptr::_pointer (you might leave that up to the destructor).
// counted_ptr owns its pointer an will free it when appropriate.
template <typename T>
class counted_ptr {
private:
T *_pointer;
size_t _count;
// Make copying illegal
explicit counted_ptr(const counted_ptr&);
counted_ptr& operator=(const counted_ptr<T>& p);
public:
counted_ptr(T* p=0, size_t c=1);
~counted_ptr();
void retain(); // increase reference count.
bool release(); // decrease reference count. Return true iff count is 0
void reassign(T *p); // point to something else.
size_t count() const;
counted_ptr& operator=(T* p);
T& operator*() const;
T* operator->() const;
};
template <typename T>
class smart_ptr {
private:
counted_ptr<T> *_shared;
void release(); // release the shared pointer
void retain(); // retain the shared pointer
public:
smart_ptr(T* p=0, int c=1); // make a smart_ptr that points to p
explicit smart_ptr(counted_ptr<T>& p); // make a smart_ptr that shares p
explicit smart_ptr(smart_ptr& p); // copy constructor
~smart_ptr();
// note: a smart_ptr's brethren are the smart_ptrs that share a counted_ptr.
smart_ptr& operator=(smart_ptr& p); /* Join p's brethren. Doesn't alter pre-call
* brethren. p is non-const because this->_shared can't be const. */
smart_ptr& operator=(counted_ptr<T>& p); /* Share p. Doesn't alter brethren.
* p is non-const because *this isn't const. */
smart_ptr& operator=(T* p); // repoint this pointer. Alters brethren
size_t count() const; // reference count
T& operator*() const; // delegate these to _shared
T* operator->() const;
};
Hopefully, the only ambiguous points above are the intentional ones.
(Sorry, newbie here, and can't leave comments). What Adatapost added, "one=new double(5);" should work. One other change needed, though: the reference counting needs a little help.
...
~counted_ptr() {
--*count;
// deallocate objects whose last reference is gone.
if (!*count)
{
delete pointer;
delete count;
}
}
counted_ptr& operator=(const counted_ptr& p)
{
// be careful to accommodate self assignment
++*p.count;
// may lose a reference here
--*count;
if (!*count)
{
delete pointer;
delete count;
}
count=p.count;
pointer=p.pointer;
return *this;
}
Of course, there's some code repetition here. It might make sense to refactor that code into its own function, e.g.
private:
/** remove our reference */
void release()
{
--*count;
if (!*count)
{
delete pointer;
delete count;
}
}
Did you, perhaps, mean "one.pointer=new double(5);"? Writing "one->pointer=new double(5);" invokes counted_ptr<double>::operator->. That is, it is approximately equivalent to:
double *tmp = one.operator->(); // returns one.pointer
tmp->pointer = new double(5);
But a double pointer isn't a structure, and so it doesn't have a pointer member.
Since the immediate problem has already been solved, I want to offer something more long term:
As you continue to develop this code, you'll definitely want to offer it up for full review by experienced programmers, whether here or elsewhere. There were a few obvious problems with your code as you posted it, though outis has helped correct them. But even once your code all compiles and seems to work in your own tests, there may be tests and situations which you haven't yet learned to think about. Smart pointers can easily have subtle problems that don't show up until very specific situations. So you'll want others to look over your code to find anything which you may have missed.
Please don't take this as any kind of insult towards your current code. I'm just offering this as friendly advice to ensure you learn the most you can out of this project.
Unless you are not doing this for academic reasons, you might want to use consider using the use_count() member of boost::shared_ptr. It's not entirely efficient, but it does work and you're better off using something well tested, mature, and thread safe. If you are doing this for learning purposes, be sure to check out the treatment of Reference Counting and Smart Pointers in More Effective C++.
You need to decrement the count and possibly delete the pointer to the old value in operator = before you overwrite it. You also need 'delete count' everywhere you have 'delete pointer' to avoid leaking memory