Can't understand this problem with ref counting class?

A

Angus

Here is my code

///reference counting
struct ref_type
{
ref_type() : cnt(0) {}

int ref_add()
{
return ++cnt;
}
int ref_remove()
{
return --cnt;
}

unsigned ref_cnt() const { return cnt; }

private:
unsigned cnt;
};

template <class T>
// T must derive from ref_type
// or supply methods ref_add and ref_remove
struct ref_ptr
{
ref_ptr() : ptr(0) {}
ref_ptr(T* p) : ptr(p) { if(p) p->ref_add(); }
ref_ptr(const ref_ptr& that) : ptr(that.ptr) { if(ptr) ptr->ref_add
(); }

~ref_ptr() { dereference(); }

T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
operator T*() const { return ptr; }

void reset() { dereference(); ptr = 0; }

void swap(ref_ptr& that) { std::swap(ptr, that.ptr); }

T* release() //dereference without deleting
{
T* p = ptr;
if(p) {
p->ref_remove();
ptr = 0;
}
return p;
}

ref_ptr& operator=(const ref_ptr& that)
{
if(&that!=this && that.ptr!=ptr) {
dereference();
if((ptr = that.ptr)!=0)
ptr->ref_add();
}
return *this;
}

bool operator==(const ref_ptr& that) const { return ptr ==
that.ptr; }
bool operator==(T* that) const { return ptr == that; }

bool operator!=(const ref_ptr& that) const { return ptr !=
that.ptr; }
bool operator!=(T* that) const { return ptr != that; }

bool operator<(const ref_ptr& that) const { return ptr <
that.ptr; }
bool operator<(T* that) const { return ptr < that; }

private:
T* ptr;

void dereference()
{
if (ptr!=0 && ptr->ref_remove()==0)
delete ptr;
}
};
//template <class T> END

template<class T>
void swap(ref_ptr<T>& a, ref_ptr<T>& b) { a.swap(b); }


class test : public ref_type
{
public:
void set(int val){ m_value = val; }
int get() const { return m_value; }

typedef ref_ptr<test> ptr_type;
private:
int m_value;
};

int main(){
test::ptr_type pmytest = new test;
pmytest->set(4);
test::ptr_type pTest1(pmytest); //increments ref count


//in above code line test::ptr_type pTest1(pmytest); correctly
increments ref counter to 2

//however, if I use like this: test* pmytest = new test; then ref
counter is not incremented to 2. Why?

return 0;
}

For convenience I want to be able to use more convenient test* pTest =
new test; - and for ref counter to be incremented. It works if use
test::ptr_type. But test inherits from ref_type so why does it not
work?

Angus
 
F

Francesco

Here is my code

///reference counting
struct ref_type
{
ref_type() : cnt(0) {}

int ref_add()
{
return ++cnt;
}
int ref_remove()
{
return --cnt;
}

unsigned ref_cnt() const { return cnt; }

private:
unsigned cnt;

};

template <class T>
// T must derive from ref_type
// or supply methods ref_add and ref_remove
struct ref_ptr
{
ref_ptr() : ptr(0) {}
ref_ptr(T* p) : ptr(p) { if(p) p->ref_add(); }
ref_ptr(const ref_ptr& that) : ptr(that.ptr) { if(ptr) ptr->ref_add
(); }

~ref_ptr() { dereference(); }

T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
operator T*() const { return ptr; }

void reset() { dereference(); ptr = 0; }

void swap(ref_ptr& that) { std::swap(ptr, that.ptr); }

T* release() //dereference without deleting
{
T* p = ptr;
if(p) {
p->ref_remove();
ptr = 0;
}
return p;
}

ref_ptr& operator=(const ref_ptr& that)
{
if(&that!=this && that.ptr!=ptr) {
dereference();
if((ptr = that.ptr)!=0)
ptr->ref_add();
}
return *this;
}

bool operator==(const ref_ptr& that) const { return ptr ==
that.ptr; }
bool operator==(T* that) const { return ptr == that; }

bool operator!=(const ref_ptr& that) const { return ptr !=
that.ptr; }
bool operator!=(T* that) const { return ptr != that; }

bool operator<(const ref_ptr& that) const { return ptr <
that.ptr; }
bool operator<(T* that) const { return ptr < that; }

private:
T* ptr;

void dereference()
{
if (ptr!=0 && ptr->ref_remove()==0)
delete ptr;
}
};
//template <class T> END

template<class T>
void swap(ref_ptr<T>& a, ref_ptr<T>& b) { a.swap(b); }

class test : public ref_type
{
public:
void set(int val){ m_value = val; }
int get() const { return m_value; }

typedef ref_ptr<test> ptr_type;
private:
int m_value;

};

int main(){
test::ptr_type pmytest = new test;
pmytest->set(4);
test::ptr_type pTest1(pmytest); //increments ref count

//in above code line test::ptr_type pTest1(pmytest); correctly
increments ref counter to 2

//however, if I use like this: test* pmytest = new test; then ref
counter is not incremented to 2. Why?

return 0;

}

For convenience I want to be able to use more convenient test* pTest =
new test; - and for ref counter to be incremented. It works if use
test::ptr_type. But test inherits from ref_type so why does it not
work?

Angus

First of all, you should post complete code to facilitate people
testing it.
You missed to add the include and the actual test that demonstrates
your problem.

If I got it right, here is my response: I suppose that's because the
counter increment is done by the "template struct ref_ptr" - actually,
by the specific "ref_ptr<test> ptr_type" typedef. It's that template
that calls ref_add().

When you create a normal pointer with "test* pmytest = new test",
you're not actually using the template, hence those functions aren't
called at all.

Hope that helps,
Francesco.
 
G

Gil

For convenience I want to be able to use more convenient test* pTest =
new test; - and for ref counter to be incremented.  It works if use
test::ptr_type.  But test inherits from ref_type so why does it not
work?

test* pTest = new test;

the above just calls the default constructor of class test (and
operator new but that's unrelated) which in turn calls default
constructor of struct ref_type.

neither increment the reference count. when you define a raw pointer
like above there is no reference counted pointer involved.

gil
 
F

Francesco

test* pTest = new test;

the above just calls the default constructor of class test (and
operator new but that's unrelated) which in turn calls default
constructor of struct ref_type.

neither increment the reference count. when you define a raw pointer
like above there is no reference counted pointer involved.

gil

Well, we said more or less the same things, just using different
wording.

For the sake of clarity this is good, but unfortunately too many OPs
simply couldn't care less about giving a feedback after having their
issue resolved... I think I'll have to get accustomed to this, but it
would be nice to be proved wrong in this particular case.

Francesco
 
A

Angus

Well, we said more or less the same things, just using different
wording.

For the sake of clarity this is good, but unfortunately too many OPs
simply couldn't care less about giving a feedback after having their
issue resolved... I think I'll have to get accustomed to this, but it
would be nice to be proved wrong in this particular case.

Francesco- Hide quoted text -

- Show quoted text -

Would it be possible to create a default constructor which would
addref when a new (in this case) test object was created? That is
really what I am asking.
 
F

Francesco

Would it be possible to create a default constructor which would
addref when a new (in this case) test object was created? That is
really what I am asking.

If your target is to have the "cnt" variable set to 1 after that the
"test" object is created (using the "new" operator or by simply
creating a local object), you can either set it to 1 in the "ref_type"
constructor _OR_, otherwise, change the "cnt" variable from private to
protected and increment it in the "test" constructor.

Be aware, though, that even after applying my suggestions, your
classes will still be messy and easy to misuse.

For example, applying either of my suggestions, after executing:

-------
test* ptest = new test;
-------

"cnt" will be set to 1, but after executing:

-------
test::ptr_type pmytest = new test;
-------

"cnt" will be set to 2.

Be really careful if you're going to use such a counter to destroy
dynamic objects, because declaring a local "test t;" then "t.cnt" will
equal to 1 anyway.

If I get it right, the real thing you want to do is to override
"operator new()" inside of your "test" class, so that the counter gets
incremented from 0 to 1 only when a dynamic "test" object gets
created.

In such a case you would be better waiting for more knowledgeable
advice than mine - assuming you can't do it safely by yourself.

You could get even better help if you clarify what the counter will be
actually used for.

Hope that helps,
cheers,
Francesco
 
F

Francesco

If I get it right, the real thing you want to do is to override
"operator new()" inside of your "test" class, so that the counter gets
incremented from 0 to 1 only when a dynamic "test" object gets
created.

I've just realized that this approach cannot be used: "operator new"
will be a static member and will not be able to increment the counter
- there will be no object to modify inside the function.

Maybe then you could make all the constructors for the "test" class
inaccessible to public and relegate the operation of creating
instances to a factory class/function - in this way, you'll be sure
you'll never (be allowed to) create a local "test" object.

I'm eager to read comments about my lucubrations, though :-/

Cheers,
Francesco
 
A

Angus

I've just realized that this approach cannot be used: "operator new"
will be a static member and will not be able to increment the counter
- there will be no object to modify inside the function.

Maybe then you could make all the constructors for the "test" class
inaccessible to public and relegate the operation of creating
instances to a factory class/function - in this way, you'll be sure
you'll never (be allowed to) create a local "test" object.

I'm eager to read comments about my lucubrations, though :-/

Cheers,
Francesco

I really cannot comment on your lucubrations but in view of these
responses I think I will have to use test::ptr_type pmytest = new
test;. It is not so bad after all.

The line:
test::ptr_type pTest1(pmytest); //increments ref count

is simply used to addref - because it is possible that the object
might be destroyed (or attempt to be destroyed in the function (or
even by another process) and if object is referenced further down
function I will get a crash. Just a safety thing really.
 
F

Francesco

I really cannot comment on your lucubrations

And nobody could ever blame you for that ;-) Heck, my suggestion to
override "operator new()" was really a BadMove(TM)... luckily, I'm
learning not to invoke "type()" (and moreover "submit()") when the
actual data might come from corrupted memory (read "garbage") ;-)
but in view of these
responses I think I will have to use test::ptr_type pmytest = new
test;.  It is not so bad after all.

The line:
test::ptr_type pTest1(pmytest); //increments ref count

is simply used to addref - because it is possible that the object
might be destroyed (or attempt to be destroyed in the function (or
even by another process) and if object is referenced further down
function I will get a crash.  Just a safety thing really.

As I've been told recently, you could consider making the dtor private
or protected, which is an alternative for factories when in need to
ensure dynamic allocation and to relegate the destruction to a limited
number of locations that you might keep under your control - shall you
find these things useful and fitting to your needs.

There is quite a long and interesting thread active and running about
all these issues, you might consider having a look.

Hope that helps, have good coding.

Francesco
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top