fix this bug and i will clean your gutters

Started by
4 comments, last by Dave Hunt 17 years, 6 months ago
This is a problem that I have been staring at with befuddlement & confusion for 24 hours, and I've given up, something I rarely do. I am making an *extremely simple* stack implementation, one that simply uses an array of fixed size (128 integers) to store my data. Do not worry about this primitive design -- it is this "undefined reference" error I am having trouble with, which pertains to a static data member. I have three files: driver.cpp, Stack.h, and Stack.cpp. When I compile them (using g++) I get undefined reference errors for my 'numInstances' data member. If I remove the static member's declaration, definition and useage altogether the program compiles fine. Furthermore, when I combine all three files into one (with minor modifications of course, such as removing #IFNDEFs, multiple #include statements, etc.) file called test.cpp and compile it, it compiles perfectly! After reviewing my source code, as I believe you will agree, there must be some problem with my Stack.h file. As to what this problem is, I do not know. Stack.h

#include <iostream>

#ifndef MY_STACK
#define MY_STACK

const int STACK_CAPACITY = 128;
typedef int StackElement;

class myStack
{
public:
        static int numInstances;

        // Constructor
        myStack();
        ~myStack();

        bool empty() const;
        void push(const StackElement& value);
        void display(ostream& out) const;
        StackElement top() const;
        void pop();

private:
        // Data members
        StackElement myArray[STACK_CAPACITY];
        int myTop;
};

#endif

Stack.cpp

#include <iostream>
using namespace std;

#include "Stack.h"

// Definition of Stack constructor.
myStack::myStack()
{
        myTop = -1;
        //numInstances++;
}

// Default destructor
myStack::~myStack()
{
        //numInstances--;
}

// Definition of empty()
bool myStack::empty() const
{
        return (myTop == -1);
}

// Definition of push()
void myStack::push(const StackElement& value)
{
        if(myTop < STACK_CAPACITY - 1) // Preserve stack invariant
        {
                ++myTop;
                myArray[myTop] = value;
        }

        else
        {
                cerr << "*** Stack full -- can't add new value ***\n"
                        "Must increase value of STACK_CAPACITY in Stack.h\n";
                exit(1);
        }
}

// Definition of display()
void myStack::display(ostream& out) const
{
        for(int i = myTop; i >= 0; i--)
                out << myArray << endl;
}

// Definition of top()
StackElement myStack::top() const
{
        if(!empty())
                return (myArray[myTop]);

        else
        {
                cerr << "*** Stack is empty -- returning garbage value ***\n";
                return (myArray[STACK_CAPACITY - 1]);
        }
}

// Definition of pop()
void myStack::pop()
{
        if(!empty())
                myTop--;

        else
                cerr << "*** Stack is empty -- can't remove a value ***\n";
}

driver.cpp

#include <iostream>
using namespace std;

#include "Stack.h"

int main()
{
        unsigned number, remainder;
        myStack stackOfRemainders;
        char response;

        stackOfRemainders.numInstances = 0;

        do
        {
                cout << "Enter positive integer to convert : ";
                cin >> number;
                while(number != 0)
                {
                        remainder = number % 2;
                        stackOfRemainders.push(remainder);
                        number /= 2;
                }

                cout << "Base-2 Representation : ";
 
               while(!stackOfRemainders.empty())
                {
                        remainder = stackOfRemainders.top();
                        stackOfRemainders.pop();
                        cout << remainder;
                }

                cout << endl;
                cout << "\nMore (Y or N)? ";
                cin >> response;
        } while(response == 'Y' || response == 'y');
}

Like I said, if you put them all into one file they will compile fine, so why is numInstances an undefined reference when implemented into three separate files?
Well I believe in God, and the only thing that scares me is Keyser Soze.
Advertisement
Add

int myStack::numInstances;

or

int myStack::numInstances = 0; // initialize it to something

to your implementation file.

Gizz
Whilst you declare numInstances here:

class myStack{public:        static int numInstances;...


You do not seem to actually define it anywhere. You need to put
int myStack::numInstances = 0;
into Stack.cpp.
Gizz and bakery2k1 you were both very close but bakery2k1 specified for me to initialize numInstances in the Stack.cpp file, not the implementation...

And as it turns out, bakery2k1 was right.

bakery2k1, hisDudeness owes you one Gutter Clean. ;-)
Well I believe in God, and the only thing that scares me is Keyser Soze.
Quote:Original post by hisDudeness
When I compile them (using g++) I get undefined reference errors for my 'numInstances' data member. If I remove the static member's declaration, definition and useage altogether the program compiles fine.


When you write class foo { static int bar; };, foo::bar is merely declared: you are telling the compiler "somewhere in my code, there is an integer variable named foo::bar". It doesn't actually define (i.e. create) the variable. It's pretty much the same situation as with extern.

In fact, when you see class foo { static int bar; }; you should really read class foo { extern static int bar; }; (no, that's not legal C++).

Keep in mind that when you define a class, no variable is ever created. Non-static member variables themselves are only created when you construct an object of that type! If the compiler were to define static member variables when the class is defined, you would end up with a copy of that variable for every translation unit that defines the class (through the magic of header files). Which would be bad (and is what happens with file-scope static variables).

So, instead, the compiler leaves it up to you to actually define the static wherever you want it to. You get to choose the module you want to put it in and need to make sure that the variable is defined once and only once.

See the C++ FAQ lite. In fact, everyone here should read that whole FAQ over carefully at least once.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by hisDudeness
Gizz and bakery2k1 you were both very close but bakery2k1 specified for me to initialize numInstances in the Stack.cpp file, not the implementation...

And as it turns out, bakery2k1 was right.

bakery2k1, hisDudeness owes you one Gutter Clean. ;-)


Stack.cpp is the implementation file (of the Stack class). I think you owe them both a gutter cleaning ;)

This topic is closed to new replies.

Advertisement