Brenden Price
Brenden Price

Reputation: 537

C++ Class Inheritance, Undefined Reference

I'm creating a class hierarchy of two dimensional shapes. The base class is a Quadrilateral, Trapezoid and Parallelogram inherit Quadrilateral, Rectangle inherits Parallelogram, and Square inherits Rectangle. I'm attempting to test my classes, so I can correct any errors I find, but I'm receiving a compiling error. This is the relevant sections pertaining to my error:

Shapes.h:

#ifndef Shapes_h
#define Shapes_h

class Point {
public:
  Point();
  Point(int, int);
  int getX();
  int getY();
  void setValues();
private:
  int x;
  int y;
};

class Quadrilateral {
public:
    Quadrilateral();
    Quadrilateral(Point, Point, Point, Point);
    void print();
private:
  Point point1;
  Point point2;
  Point point3;
  Point point4;
};

class Parallelogram : public Quadrilateral {
public:
  Parallelogram();
  Parallelogram(Point, Point, int);
  void print();
private:
  Point point1;
  Point point2;
  Point point3;
  Point point4;
};

class Trapezoid : public Quadrilateral {
public:
  Trapezoid();
  Trapezoid(Point, Point, int, int);
  void print();
private:
  Point point1;
  Point point2;
  Point point3;
  Point point4;
};

class Rectangle : public Parallelogram {
public:
  Rectangle();
  Rectangle(Point, int, int);
  void print();
private:
  Point point1;
  Point point2;
  Point point3;
  Point point4;
};

class Square : public Rectangle {
public:
  Square();
  Square(Point, int);
  void print();
private:
  Point point1;
  Point point2;
  Point point3;
  Point point4;
};

#endif /* Shapes_h */

Point.cpp:

#include "Shapes.h"
#include <iostream>
using namespace std;

Point::Point() {
  this->x = 0;
  this->y = 0;
}

Point::Point(int x, int y){
  this->x = x;
  this->y = y;
}

int Point::getX(){
  return this->x;
}

int Point::getY(){
  return this->y;
}

Square.cpp:

#include "Shapes.h"
#include <iostream>
using namespace std;

Square::Square(Point a, int len):Rectangle(a, len, len) {

}

Rectangle.cpp (File giving me the error):

#include "Shapes.h"
#include <iostream>
using namespace std;

Rectangle::Rectangle(Point a, int len, int height):Parallelogram(a, Point(a.getX(), a.getY()+height), len) {

}

Parallelogram.cpp:

#include "Shapes.h"
#include <iostream>
using namespace std;

Parallelogram::Parallelogram(Point a, Point b, int len):Quadrilateral(a, b, Point(a.getX()+len,a.getY()), Point(b.getX()+len, b.getY())) {

}

Quadrilateral.cpp:

#include "Shapes.h"
#include <iostream>
using namespace std;

Quadrilateral::Quadrilateral(){
  this->point1 = Point(0,0);
  this->point2 = Point(0,1);
  this->point3 = Point(1,0);
  this->point4 = Point(1,1);
}

Quadrilateral::Quadrilateral(Point a, Point b, Point c, Point d) {
  this->point1 = a;
  this->point2 = b;
  this->point3 = c;
  this->point4 = d;
}

Test Driver:

#include "Shapes.h"
#include <iostream>
using namespace std;

int main() {
  Square square = Square(Point(1,1),2);
  square.print();
  return 0;
}

Command Line Compile Error:

g++ -Wall -Wextra -c *.cpp
g++ -o *.o
/usr/bin/ld: Rectangle.o: in function `Rectangle::Rectangle(Point, int, int)':
Rectangle.cpp:(.text+0x74): undefined reference to `Parallelogram::Parallelogram(Point, Point, int)'
collect2: error: ld returned 1 exit status

So, as far as I can tell, Square is successfully able to call the Rectangle constructor, but Rectangle is failing to call Parallelogram.

Upvotes: 0

Views: 310

Answers (2)

Brenden Price
Brenden Price

Reputation: 537

The issue here was with the compiling command given. The -o flag allows you to name the outputted executable. Because you didn't prove a name, it is taking the first file provided in *.o and overwriting it, using it as the file name. Because it was overwritten, it breaks the compilation of the other files. The solution here would be to provide a name or remove the -o flag.

Upvotes: 0

UweJ
UweJ

Reputation: 489

You can improve your code by:

  • Using the points declared for Quadrilateral in the inheritances. The ones defined in the Parallelogram, Trapezoid, Rectangle and Square will never be used or correctly initialized.
  • Delete default constructors which are not needed. You can also easily not declare/define them, then they will be hidden.
  • Dedicated header file for each class
  • Using namespaces makes it easier if you include a library which also defines a class Point
  • Using auto

Point.hpp

#ifndef Point_h
#define Point_h

namespace Shape {
  class Point {
    public:
    Point();
    Point(int x, int y);
    int getX();
    int getY();
    std::string toString();
    
    private:
    int x;
    int y;
  };
}

#endif /* Point_h */

Point.cpp

#include <Point>
#include <iostream>
#include <sstream>
#include <string>

namespace Shape {
  Point::Point() {
    this -> x = 0;
    this -> y = 0;
  }
  Point::Point(int x, int y) {
    this -> x = x;
    this -> y = y;
  }
  int Point::getX() {
    return this -> x;
  }
  int Point::getY() {
    return this -> y;
  }
  std::string Point::toString() {
    std::ostringstream oss;
    oss << "Point(";
    oss << x;
    oss << ", ";
    oss << y;
    oss << ")";
    return oss.str();
  }
}

Quadrilateral.hpp

#ifndef Quadrilateral_h
#define Quadrilateral_h

namespace Shape {
  class Quadrilateral {
    public:
    Quadrilateral();
    Quadrilateral(Point a, Point b, Point c, Point d);
    void print();
    
    protected:
    Point point1;
    Point point2;
    Point point3;
    Point point4;
  };
}
#endif /* Quadrilateral_h */

Quadrilateral.cpp

#include <Quadrilateral>
#include <iostream>

namespace Shape {
  Quadrilateral::Quadrilateral() {
    this -> point1 = Point(0, 0);
    this -> point2 = Point(0, 1);
    this -> point3 = Point(1, 0);
    this -> point4 = Point(1, 1);
  }
  Quadrilateral::Quadrilateral(Point a, Point b, Point c, Point d) {
    this -> point1 = a;
    this -> point2 = b;
    this -> point3 = c;
    this -> point4 = d;
  }
  void Quadrilateral::print() {
      std::cout<<"Quadrilateral: "<<point1.toString()<<", "<<point2.toString()<<", "<<point3.toString()<<", "<<point4.toString()<<std::endl;
  }
}

Parallelogram.hpp

#ifndef Parallelogram_h
#define Parallelogram_h

#include <Quadrilateral>
#include <iostream>

namespace Shape {
  class Parallelogram: public Quadrilateral {
    public:
    Parallelogram() = delete;
    Parallelogram(Point a, Point b, int len);
    void print();
  };
}

#endif /* Parallelogram_h */

Parallelogram.cpp

#include <Parallelogram>
#include <iostream>

namespace Shape {
    Parallelogram::Parallelogram(Point a, Point b, int len): Quadrilateral(a, b, Point(a.getX() + len, a.getY()), Point(b.getX() + len, b.getY())) {}

    void Parallelogram::print() {
      std::cout<<"Parallelogram: "<<point1.toString()<<", "<<point2.toString()<<", "<<point3.toString()<<", "<<point4.toString()<<std::endl;
    }
}

Trapezoid.hpp

#ifndef Trapezoid_h
#define Trapezoid_h

#include <Quadrilateral>
#include <iostream>

namespace Shape {
  class Trapezoid: public Quadrilateral {
    public:
    Trapezoid() = delete; 
    Trapezoid(Point a, Point b, int len1, int len2);
    void print();
  };
}
#endif /* Trapezoid_h*/

Trapezoid.cpp

#include <Trapezoid>
#include <iostream>

namespace Shape {
  Trapezoid::Trapezoid(Point a, Point b, int len1, int len2): Quadrilateral(a, Point(a.getX(), a.getY()+len1), b, Point(b.getX(), b.getY()+len2)) {}

  void Trapezoid::print() {
      std::cout<<"Trapezoid: "<<point1.toString()<<", "<<point2.toString()<<", "<<point3.toString()<<", "<<point4.toString()<<std::endl;
  }
}

Rectangle.hpp

#ifndef Rectangle_h
#define Rectangle_h

#include <Parallelogram>

namespace Shape {
  class Rectangle: public Parallelogram {
    public:
    Rectangle() = delete; 
    Rectangle(Point a, int len, int height);
    void print();
  };
}

#endif /* Rectangle_h*/

Rectangle.cpp

#include <Rectangle>
#include <iostream>

namespace Shape {
  Rectangle::Rectangle(Point a, int len, int height): Parallelogram(a, Point(a.getX(), a.getY() + height), len) {}

  void Rectangle::print() {
      std::cout<<"Rectangle: "<<point1.toString()<<", "<<point2.toString()<<", "<<point3.toString()<<", "<<point4.toString()<<std::endl;
  }
}

Square.hpp

#ifndef Square_h
#define Square_h

#include <Rectangle>

namespace Shape {
  class Square: public Rectangle {
    public:
    Square() = delete;
    Square(Point a, int len);
    void print();
  };
}

#endif /* Square_h*/

Square.cpp

#include <Square>
#include <iostream>

namespace Shape {
  Square::Square(Point a, int len): Rectangle(a, len, len) {}

  void Square::print() {
      std::cout<<"Square: "<<point1.toString()<<", "<<point2.toString()<<", "<<point3.toString()<<", "<<point4.toString()<<std::endl;
  }
}

main.cpp

#include <Square>
#include <Trapezoid>

int main()
{
  auto square1 = Shape::Square(Point(1,1),2);
  square1.print();
  auto trapezoid1 = Shape::Trapezoid(Point(1,1), Point(2,0), 3, 5);
  trapezoid1.print();

  return 0;
}

Output will be:

Square: Point(1, 1), Point(1, 3), Point(3, 1), Point(3, 3)
Trapezoid: Point(1, 1), Point(1, 4), Point(2, 0), Point(2, 5)

Upvotes: 1

Related Questions