newbie -- smart pointer destructor called without destructor everbeing called

Discussion in 'C++' started by Jimmy Hartzell, May 19, 2008.

  1. 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
    Jimmy Hartzell, May 19, 2008
    #1
    1. Advertising

  2. Re: newbie -- smart pointer destructor called without destructorever being called

    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?
    Jimmy Hartzell, May 20, 2008
    #2
    1. Advertising

  3. Re: newbie -- smart pointer destructor called without destructorever being called

    Jimmy Hartzell wrote:
    > 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.
    Jimmy Hartzell, May 20, 2008
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    418
  2. coala
    Replies:
    3
    Views:
    362
    coala
    Sep 6, 2006
  3. coala
    Replies:
    1
    Views:
    578
    Victor Bazarov
    Sep 6, 2006
  4. Jimmy Hartzell
    Replies:
    0
    Views:
    411
    Jimmy Hartzell
    May 19, 2008
  5. Hicham Mouline
    Replies:
    100
    Views:
    2,033
    Noah Roberts
    Aug 25, 2009
Loading...

Share This Page