private construction on GCC

B

brianhray

I get an access exception on Release() on GCC but worked on different
compiler, I am trying to figure out if there is anything with this code
in a header:

class CountObject {
private:
long count;
public:
CountObject() : count(0) {}
CountObject(const CountObject&) : count(0) {}
virtual ~CountObject() {}
CountObject& operator=(const CountObject &) { return *this; }

void Grab() { count++; }
void Release() {
if (count > 0) count--; //< Exception Here
if (count <= 0) {
delete this;
}
}

// attribute functions
long Count() const { return count; }
};


Any insight would be helpful. What is the best way (most standard) to
prevent this problem.

Regards, Brian
 
V

Victor Bazarov

I get an access exception on Release() on GCC but worked on different
compiler, I am trying to figure out if there is anything with this
code in a header:

class CountObject {
private:
long count;
public:
CountObject() : count(0) {}
CountObject(const CountObject&) : count(0) {}
virtual ~CountObject() {}
CountObject& operator=(const CountObject &) { return *this; }

void Grab() { count++; }
void Release() {
if (count > 0) count--; //< Exception Here
if (count <= 0) {
delete this;
}
}

// attribute functions
long Count() const { return count; }
};


Any insight would be helpful. What is the best way (most standard) to
prevent this problem.

You're apparently invoking undefined behaviour. Most likely when you
call 'Release', the object for which you call is not valid, like if you
do it with a pointer and the pointer is null, or a reference to an object
that has been disposed of.

V
 
K

Kai-Uwe Bux

I get an access exception on Release() on GCC but worked on different
compiler,

Ok, sounds like undefined behavior.
I am trying to figure out if there is anything with this code
in a header:

class CountObject {
private:
long count;
public:
CountObject() : count(0) {}
CountObject(const CountObject&) : count(0) {}
virtual ~CountObject() {}
CountObject& operator=(const CountObject &) { return *this; }

void Grab() { count++; }
void Release() {
if (count > 0) count--; //< Exception Here
if (count <= 0) {
delete this;
}
}

// attribute functions
long Count() const { return count; }
};


Any insight would be helpful. What is the best way (most standard) to
prevent this problem.

The code you posted, does not show any problems. However, calling

delete this;

can be tricky. If I was to take a wild guess, I would conjecture that in
your other code, you call Release() (or some other member function) on an
object that has already self-detonated. In that case, you enter UB.

Another catch with "delete this" is that is presupposes the object has been
created via new(). Maybe you call Release() on an object on the stack
causing UB that way.

Please, post a minimal complete program that reproduces the problem. Then,
we can set the guess work asside and get to the heart of the matter.


Best

Kai-Uwe Bux
 
K

Kaz Kylheku

void Release() {
if (count > 0) count--; //< Exception Here
if (count <= 0) {
delete this;
}
}

That is incredibly stupid coding. If the count is zero on entry into
the Release() function, that is an error. So the logic is basically:

if (there isn't a software defect here from a mismanaged refcount)
decrement the refcount

if (the refcount is now zero, or it has been mismanaged)
blow away the object

In other words if there is an error situation, the logic compounds it
by trying to delete the object, something which already happened.

If you aren't going to handle an error situation in a meaningful way,
and if you aren't going to at least report it in an error log, then
don't test for it! Just write:

void Release()
{
if (--count == 0)
delete this;
}

// attribute functions
long Count() const { return count; }

If any part of the program, other than the Grab() and Release()
functions, cares what the absolute reference count is, you are probably
doing something wrong.
 
K

Kaz Kylheku

void Release() {
if (count > 0) count--; //< Exception Here
if (count <= 0) {
delete this;
}
}

Your function here checks for error conditions without handling them.

If the count is non-positive on entry into the function, then your
program contains a software defect.

The line

if (count > 0) count--;

essentially says:

if (the refcount has not been mismanaged)
decrement it

What's the point of testing for an error condition which you don't
intend to handle, or at least report in an error log?

The next statement is even worse:

if (count <= 0) {
delete this;
}

In other words:

if (the refcount is now zero, or has been mismanaged)
blow away the object;

You have ensured that if there is a refcount mismanagement problem in
the program, it will not only be silently ignored, it will actually be
compounded by attempts to free the object two or more times.

How would the refcount ever drop below zero, unless the object were
corrupt? It's only manipulated by the object construction and by Grab()
and Release(). Release() ensures that the count cannot drop below zero,
so why would you check using <= 0?
// attribute functions
long Count() const { return count; }

Nothing in the program should care about the absolute reference count,
so the presence of a public function which returns it is suspicious.
 
M

Mark P

Kai-Uwe Bux said:
Ok, sounds like undefined behavior.


The code you posted, does not show any problems. However, calling

delete this;

can be tricky. If I was to take a wild guess, I would conjecture that in
your other code, you call Release() (or some other member function) on an
object that has already self-detonated. In that case, you enter UB.

Another catch with "delete this" is that is presupposes the object has been
created via new(). Maybe you call Release() on an object on the stack
causing UB that way.

That would be my first guess too. To the OP, if you're going to allow
the object to delete itself then it's a good idea to enforce that all
instances are dynamically allocated. One such approach is explained in
the FAQ:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.21
 
A

Andrey Tarasevich

I get an access exception on Release() on GCC but worked on different
compiler, I am trying to figure out if there is anything with this code
in a header:

class CountObject {
private:
long count;
public:
CountObject() : count(0) {}
CountObject(const CountObject&) : count(0) {}
virtual ~CountObject() {}
CountObject& operator=(const CountObject &) { return *this; }

void Grab() { count++; }
void Release() {
if (count > 0) count--; //< Exception Here
if (count <= 0) {
delete this;
}
}

// attribute functions
long Count() const { return count; }
};


Any insight would be helpful. What is the best way (most standard) to
prevent this problem.
...

Well, if you do something like this

CountObject* p = new CountObject;
p->Grab();
p->Release();
p->Release();

(i.e. call 'Release' more times than you call 'Grab') the second call to
'Release' will lead to undefined behavior, since the object has already been
destroyed, which in practice will often crash at the first attempt to access any
data field inside 'Release'. This could be exactly what happens in your case.

A couple of words on the design of your code. The implementation of your
'Release' method begins with 'if (count > 0)', which means that you expect that
'Release' can be called with 'count' equal to zero. This, in turn, means that
you actually expect that there might be more calls to 'Release' in your program
than calls to 'Grab'. This is, in my opinion, a rather strange thing to allow.

The way it is implemented now, the only safe scenario when you can call
'Release' with 'count' equal to zero is when the object never "grabbed" but
still "released", as in

CountObject* p = new CountObject;
...; // No 'Grab's
p->Release(); // OK, kills the object

There's no other scenario that can legally lead to the 'count == 0' situation in
'Release'. You probably have to ask yourself: do you really have to allow the
'count == 0' situation in 'Release'? I'd suggest that you better make it
illegal, i.e. require that there's an equal number of 'Grab' and 'Release' calls
in the program. In that case the 'Release' method might look as follows:

void Release() {
assert(count > 0);
if (--count == 0)
delete this;
}

This will outlaw the above 'no-Grab' variant and the user will always have to do
an explicit 'Grab' every time he creates a reference to the object:

CountObject* p = new CountObject;
p->Grab();
...
p->Release();

To make thing easier for the user it might make sense to call 'Grab' internally
from the object's constructor (i.e. essentially start with 'count' set to 1, not
0). In that case, there's no need to 'Grab' the object immediately after its
creation

CountObject* p = new CountObject;
...; // No 'Grab's
p->Release(); // OK, kills the object

and there's a need to do a 'Grab' only when extra references to the object are
introduced:

CountObject* p = new CountObject;
// No 'Grab' needed
...
CountObject* q = p;
q->Grab(); // Extra reference - do a 'Grab'
...
p->Release();
...
q->Release();

Choose which approach you like better.

Of course, there's also an option to go with a third-party reference-counting
approach instead of inventing your own (see Boost library, for example).
 
A

Andrey Tarasevich

Mark said:
...
That would be my first guess too. To the OP, if you're going to allow
the object to delete itself then it's a good idea to enforce that all
instances are dynamically allocated. One such approach is explained in
the FAQ:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.21

I don't see such a radical requirement here ("... enforce that all instances
...."). Since the OP's implementation contains a completely manual reference
counting control (i.e. 'Grab'/'Release' methods are invoked explicitly), only
the objects that have 'Grab'/'Release' applied to them have to be dynamically
allocated. Otherwise, it is perfectly safe to have a static/automatic instance
of the object. That's, BTW, often a requirement imposed on various
reference-counting implementations: they should not prohibit/interfere with
static/automatic instantiation of objects.
 
B

brianhray

I think the problem may be caused this class further upstream:

template < class T >
class ITEM_MANAGER
{
public:
ITEM_MANAGER( void );
~ITEM_MANAGER( void );
BOOL AddInfo ( T& item );
private:
std::vector< T > m_ItemList;
};

template <class T>
BOOL ITEM_MANAGER<T>::AddInfo( T& item )
{
m_ItemList.insert( m_InfoList.end(), item );

return TRUE;
} // AddInfo


The way I am using this is:

ITEM_MANAGER<CountedItem> countedItemManager;

TheItem item1, item2;
countedItemManager.AddInfo(item1);
countedItemManager.AddInfo(item2);

That TheItem has a member mCountedItem which uses its own allocator
which uses CountObject. So, I think CountObject was a red herring.

The TheItem class has a constructor that looks something like this:

TheItem::TheItem( const TheItem& rhs )
{
*this = rhs ;
}

This calls the an overwritten "=" operator member of the TheItem
Class.

At this point *this mCountedItem is unitialized and the argument (rhs)
is initialized. So, this is where starts to lead to the crash:

mCountedItem = rhs.mCountedItem;

Is there anything wrong with what is being done by ITEM_MANAGER?
 
B

brianhray

Opps...

This line:
ITEM_MANAGER<CountedItem> countedItemManager;

What I ment to put was:

ITEM_MANAGER<TheItem> countedItemManager;

That was a typo when posting.
 

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

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,139
Latest member
JamaalCald
Top