newbie -- smart pointer destructor called without destructor everbeing called

J

Jimmy Hartzell

I'm almost entirely new to C++, and so I assume I'm making a very common
blunder.

Context: I have an interpreted programming language with an interpreter
written in C (which works rather well), where we wanted to replace the
garbage collector we were using with one of our own, based off of
reference counting and cycle checking. After some experimentation, we
determined that it would be much more type-safe and easy-to-program in
if we moved the project over to C++ and used smart pointers to enforce
our invariants. Having little-to-know experience in C++, I decided to
experiment with reference-counting smart pointers and see if I could get
my own implementation to work.

I wrote a test program to test the implementation, and I got a
segmentation fault from my operating system. Inserting some debugging
code (the member variable "sentinal" and the "assert" statement), I
determined that it appears that, at some point, the software is
destructing a GCReference that it never constructed.

My implementation and test program follow:

#include <cassert>
#include <vector>

using namespace std;

class GCAllocation {
int refcount;

public:
void ref() {
__sync_fetch_and_add(&refcount,1);
}

void deref() {
assert(refcount!=0);
__sync_fetch_and_sub(&refcount,1);
if(!refcount) {
delete this;
}
}

GCAllocation() : refcount(0) {
refcount = 0;
}

virtual ~GCAllocation() {
}
};

template<class T>
class GCReference {
T *referand;
int sentinal;

void do_ref() {
if(referand) {
referand->ref();
}
}

void do_deref() {
do_deref(referand);
}

void do_deref(T *referand) {
if(referand) {
referand->deref();
}
}

public:
GCReference(T *const referand = 0) {
this->referand = referand;
sentinal = 3133;
do_ref();
}

GCReference(const GCReference<T> &ref) {
referand = ref.referand;
sentinal = 3133;
do_ref();
}

~GCReference() {
assert(sentinal==3133);
do_deref();
}

GCReference operator=(const GCReference<T>& ref) {
// not thread-safe
T* const old_ref = ref.referand;
referand = ref.referand;

// order necessary in case of self-assignment
do_ref();
do_deref(old_ref);
}

bool operator==(const GCReference<T>& ref) const {
return referand == ref.referand;
}

operator bool() const {
return referand;
}

T& operator*() const {
return *referand;
}

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

// not a cast, since that could be used by accident.
// this should only be used when absolutely necessary.
// TODO: find out how to support dynamic-casts without this
// sort of mechanism
T* get_raw_pointer() const {
return referand;
}
};

class GCTest: public virtual GCAllocation {
public:
vector<GCReference<GCTest> > refs;
GCTest() {
for(int i = 0; i < 16; ++i) {
refs.push_back(0);
}
}

void fill_all(GCReference<GCTest> what_with) {
for(int i = 0; i < 16; ++i) {
refs = what_with;
}
}
};

static void test() {
GCReference<GCTest> test1(new GCTest());
GCReference<GCTest> test2(new GCTest());
test1->fill_all(test2);
test1->fill_all(0);
test1->fill_all(test2);
test1->fill_all(0);
assert(!test1->refs[5]);
test1->fill_all(test2);
assert(test1->refs[5] == test2);
}

int main() {
while(1) {
test();
}
return 0;
}

I get the following output:
$ c++ simple.c++
$ ./a.out
a.out: simple.c++:65: GCReference<T>::~GCReference() [with T = GCTest]:
Assertion `sentinal==3133' failed.
$ c++ --versionc++ (GCC) 4.1.3 20070929 (prerelease) (Ubuntu
4.1.2-16ubuntu2)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$
Jimmy Hartzell
 
J

Jimmy Hartzell

Specifically this function:
void fill_all(GCReference<GCTest> what_with) {
for(int i = 0; i < 16; i++) {
refs = what_with;
}
}

Some destructor seems to be getting called within this function, on an
improperly constructed object, in the body of the loop (at the end of
it). Why would that be?
 
J

Jimmy Hartzell

Jimmy said:
Specifically this function:
void fill_all(GCReference<GCTest> what_with) {
for(int i = 0; i < 16; i++) {
refs = what_with;
}
}

Some destructor seems to be getting called within this function, on an
improperly constructed object, in the body of the loop (at the end of
it). Why would that be?

Right. My assignment operator wasn't returning anything. Fixed. Sorry.
 

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,733
Messages
2,569,439
Members
44,829
Latest member
PIXThurman

Latest Threads

Top