Scott
Scott

Reputation: 19

C++ Undefined reference to base class functions at link time (--not-- vtables, constructors, templates, or pure virtual functions)

I'm working on a project attempting to implement a stand-alone base class (A) that can be extended by later classes (such as B and C). I want to be able to use them all independently, hence no pure virtual functions, and I want only one copy of each class in any derived classes (hence the virtuals to avoid the Dreaded Diamond). Some classes, like D, need to combine all the previously extended classes, retaining all their functions while implementing additional ones.

For added fun I'm (stuck) using C++03, gcc 4.4.6, and make 3.81.

Everything compiles fine, but I keep getting 'undefined reference to '[base class function]' at link whenever and however I attempt to use the base class functions in the derived class.

All the answers I've been able to find so far are about incorrectly implementing pure virtual functions, trying to monkey with constructors, or have failed to implement all the members of a class (hence the vtable errors). I'm not (knowingly) doing or intending any of these things.

It would be cool if these were implemented as templates, but it's unfortunately not possible due to the complexity. I can't post the actual code, but I've mocked up examples resembling the basic structure and behavior:

Header file declaring the classes:

#ifndef CODE_H
#define CODE_H

class A
{
protected:
   int a_;
   int b_;
   int c_;

public:

   explicit A() :
      a_(0), b_(0), c_(0) {}

   A(const int X, const int Y, const int Z) :
      a_(X), b_(Y), c_(Z) {}

   A(const A& X) :
      a_(X.A()), b_(X.B()), c_(X.C()) {}

   int doA1(const A& X) const;

   int doA2() const;

   A getA() const;

   A& operator=(const A& X);

   const int& A() const {return a_;}
   const int& B() const {return b_;}
   const int& C() const {return c_;}

   //One for each operator but not shown for brevity
   A operator+(const A& X) const;
   void operator+=(const A& X);
   A operator+(const int X) const;
   void operator+=(const int X);
};

class B : public virtual A
{
protected:
   int d_;

public:

   explicit B() :
      A(), d_(0) {}

   B(const int D, A X) :
      A(X), d_(D) {}

   B(const int D) :
      d_(D) {}

   int doB1(const B& X) const;

   B getB() const;

   B& operator=(const B& X);

   const int& D() const {return d_;}

   //One for each operator but not shown for brevity.
   //int d_ doesn't need to be modified by the operators, so it's ignored in their implementation.
   B operator+(const B& X) const {return B(d_, (A::operator+(X.getA())));}
   void operator+=(const B& X)   {A::operator+=(X.getA());}
   B operator+(const int X) const; {return B(d_, (A::operator+(X)));}
   void operator+=(const int X)    {A::operator+(X);}
};

class C : public virtual A
{
protected:
   int e_;
   int f_;
   int g_;

public:

   explicit C() :
      A(), e_(0), f_(0), g_(0) {}

   C(const int U, const int V, const int W, 
     const int X, const int Y, const int Z) :
         A(U, V, W), e_(X), f_(Y), g_(Z) {}

   C(const A& W, 
     const int X, const int Y, const int Z) :
         A(W), e_(X), f_(Y), g_(Z) {}

   C(const C& X) :
         C(X.A(), X.B(), X.C()), e_(X.E()), f_(X.F()), g_(X.G()) {}

   int doC1(const C& X) const;

   C getC() const;

   C& operator=(const C& X);

   const int& E() const {return e_;}
   const int& F() const {return f_;}
   const int& G() const {return g_;}

   //One for each operator but not shown for brevity
   C operator+(const C& X) const;
   void operator+=(const C& X);
   C operator+(const int X) const;
   void operator+=(const int X);
};

class D : public virtual B, public virtual C
{
public:

   explicit D() :
      B(), C() {}

   D(const int D, C Y) :
      B(D), C(Y) {}

   int doD1(const D& X) const;

   int doD2() const;

   D getD() const;

   D& operator=(const D& X);

   //One for each operator but not shown for brevity.
   D operator+(const D& X) const {return D(d_, (C::operator+(X.getC())));}
   void operator+=(const D& X)   {C::operator+=(X.getC());}
   D operator+(const int X) const; {return D(d_, (C::operator+(X)));}
   void operator+=(const int X)    {C::operator+(X);}
};
#endif

File implementing A:

#include header.h

int A::doA1(const A& X) const
{
   int doStuff;
   //does stuff.
   return doStuff;
}

int A::doA2() const
{
   int doStuff2;
   //does stuff.
   return doStuff2;
}

A A::getA() const
{
   A out(a_, b_, c_);
   return out;
}

A& A::operator=(const A& X)
{
   if (this != &X)
   {
      a_ = X.a_;
      b_ = X.b_;
      c_ = X.c_;
   }
   return *this;
}

//One for each operator but not shown for brevity
A A::operator+(const A& X) const
{
   return A(a_ + X.a_, b_ + X.b_, c_ + X.c_);
}

void A::operator+=(const A& X)
{
   a_ += X.a_;
   b_ += X.b_;
   c_ += X.c_;
}   

 A A::operator+(const int X) const
{
   return A(a_ + X, b_ + X, c_ + X);
}

void A::operator+=(const int X)
{
   a_ += X;
   b_ += X;
   c_ += X;
}   

File implementing B:

#include header.h

int B::doB1(const B& X) const
{
   //Linker complains on both doA1 and getA, even if I qualify them or remove all the qualifiers
   return (A::doA1(X.getA()));
}

B B::getB() const
{
   //Also complains here, with or without qualifiers.
   B out(d_, A::getA());
   return out;
}

B& B::operator=(const B& X)
{
   if (this != &X)
   {
      //Also here, for presumably the same reason.
      A::operator=(D.getA());
      d_ = X.d_;
   }
   return *this;
}

I can provide mockups for C and D as well, though they follow the same basic pattern as A and B.

Based on the answers I've seen so far I should, at most, only need to add the base class qualifier to the functions to avoid any accidental name hiding, but that doesn't appear to work. Also strangely: instantiated objects can't seem to find their public functions (like in doB1). Am I just a moron missing something obvious (which I wouldn't be surprised coming from a C background)?

Upvotes: 1

Views: 2901

Answers (1)

Daniel Sęk
Daniel Sęk

Reputation: 2769

You are defining global functions instead of class methods.

In class A

int doA1(const A& X) const;
int doA2() const;

Implemetation

int A::doA1(const A& X) const
int doA2() const

Thera is no A:: in second case.

Upvotes: 2

Related Questions