Undefined reference to Line2D()

I've some problem getting Line3D to display. I get this "undefined reference to Line2D:: Line2D" when I tried to compile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include "Point2D.h"
#include "Point3D.h"
#include "Line2D.h"
#include "Line3D.h"
#include <vector>
#include <set>
#include <map>
#include <cstdlib>
#include <string>
using namespace std;

vector<Point2D>p2dList;
vector<Point3D>p3dList;
vector<Line2D>line2dList;
vector<Line3D>line3dList;

//void readData();


void readP2DData (string);
void readP3DData (string);
void readL2DData (string);
void readL3DData (string);

int main()
{
    ifstream ifile;
    ifile.open("data2.txt",ios::in);
    string data;


    int totalRecords=0;
    while(getline(ifile,data))
    {
        istringstream iss (data);
        string dType;
        getline(iss, dType, '|');

        string dVal;
        cout<<dType<<": ";


        if(dType=="Point2D")
            readP2DData(data);
        else if(dType=="Point3D")
            readP3DData(data);
        else if(dType=="Line2D")
            readL2DData(data);
        else if(dType=="Line3D")
            readL3DData(data);

        cout<<endl;

        totalRecords++;
    }
    cout<<"Total records = "<<totalRecords<<endl;

    ifile.close();
}


void readL2DData (string data)
{
    istringstream iss(data);
    string dType, dVal1, dX1, dY1, dVal2, dX2, dY2;
    getline(iss, dType, '|');
    getline(iss, dVal1, '[');
    getline(iss, dX1, ',');
    getline(iss, dY1, ']');
    getline(iss, dVal2, '[');
    getline(iss, dX2, ',');
    getline(iss, dY2, ']');

    cout<<"("<<dX1<<", "<<dY1<<"), ("<<dX2<<", "<<dY2<<")";

    int x1 = stoi (dX1);
    int y1 = stoi (dY1);
    int x2 = stoi (dX2);
    int y2 = stoi (dY2);

    Point2D p2d1 (x1, y1) ;
    Point2D p2d2 (x2, y2);
    Line2D l2d (p2d1, p2d2);

//    cout<<l2d;
}

void readL3DData (string data)
{
    istringstream iss(data);
    string dType, dVal1, dX1, dY1, dZ1, dVal2, dX2, dY2, dZ2;
    getline(iss, dType, '|');
    getline(iss, dVal1, '[');
    getline(iss, dX1, ',');
    getline(iss, dY1, ',');
    getline(iss, dZ1, ']');
    getline(iss, dVal2, '[');
    getline(iss, dX2, ',');
    getline(iss, dY2, ',');
    getline(iss, dZ2, ']');

    int x1 = stoi (dX1);
    int y1 = stoi (dY1);
    int z1 = stoi (dZ1);
    int x2 = stoi (dX1);
    int y2 = stoi (dY1);
    int z2 = stoi (dZ1);

    Point3D p3d1 (x1, y1, z1);
    Point3D p3d2 (x2, y2, z2);
    Line3D l3d (p3d1, p3d2);
}


Point2D.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#ifndef Point2D_h
#define Point2D_h
#include <iostream>
#include <math.h>
using namespace std;

class Point2D
{
protected:
    int x;
    int y;
    double distFrOrigin;

    void setDistFrOrigin()
    {
        distFrOrigin=sqrt(pow(x,2)-pow(y,2));
    }

public:
    Point2D();

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

    int getX()
    {
        return x;
    }

    int getY()
    {
        return y;
    }

    double getScalarValue()
    {
        return distFrOrigin;
    }

    void setX(int x)
    {
        this->x=x;
    }

    void setY(int y)
    {
        this->y=y;
    }

    friend ostream & operator << (ostream &os, Point2D &p2d)
    {
        cout<<"Point2D = ("<<p2d.x<<", "<<p2d.y<<")";
    }
};
#endif // Point2D_h

[code]

Point3D.h
[code]
#ifndef Point3D_h
#define Point3D_h
#include <iostream>
#include <cmath>
#include "Point2D.h"
using namespace std;

class Point3D: public Point2D
{
protected:
    int z;
    double distFrOrigin;

    void setDistFrOrigin()
    {
        distFrOrigin=sqrt(pow(x,2)+pow(y,2)+pow(z,2));
    }

public:
    Point3D();

    Point3D(int x, int y, int z): Point2D (x,y)
    {
        this->z=z;
    }

    int getZ()
    {
        return z;
    }

    void setZ(int z)
    {
        this->z=z;
    }

    friend ostream & operator << (ostream &os, Point3D &p3d)
    {
        cout<<"Point3D = ("<<p3d.x<<", "<<p3d.y<<", "<<p3d.z<<")";
    }
};
#endif // Point3D_h


Line2D.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#ifndef Line2D_h
#define Line2D_h
#include <iostream>
#include <cmath>
#include "Point2D.h"
using namespace std;

class Line2D
{
private:
    Point2D pt1;
    Point2D pt2;

protected:
    double length;

public:
    Line2D();

    Line2D(Point2D pt1, Point2D pt2): pt1(pt2), pt2(pt2)
    {
        this->pt1=pt1;
        this->pt2=pt2;
    }

    Point2D getPt1()
    {
        return pt1;
    }

    Point2D getPt2()
    {
        return pt2;
    }

    double getScalarValue()
    {
        double length=sqrt(pow(pt1.getX()-pt2.getX(),2)+pow(pt1.getY()-pt2.getY(),2));
        return length;
    }

    void setPt1(Point2D pt1)
    {
        this->pt1=pt1;
    }

    void setPt2 (Point2D pt2)
    {
        this->pt2=pt2;
    }

    friend ostream & operator << (ostream &os, Line2D &l2d)
    {
        int distance=l2d.getScalarValue();
        cout<<"Line2D = ("<<l2d.pt1.getX()<<", "<<l2d.pt1.getY()<<"), "<<l2d.pt2.getX()<<", "<<l2d.pt2.getY()<<"); DISTANCE = "<<distance<<endl;
    }
};
#endif // Line2D_h


Line3D.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#ifndef Line3D_h
#define Line3D_h
#include <iostream>
#include "Point3D.h"
#include "Line2D.h"
using namespace std;

class Line3D: public Line2D
{
private:
    Point3D pt1;
    Point3D pt2;
protected:
    double length;

    double getLength()
    {
        double length=sqrt(pow(pt1.getX()-pt2.getX(),2)+pow(pt1.getY()-pt2.getY(),2)+pow(pt1.getZ()-pt2.getZ(),2));
        return length;
    }

public:
    Line3D();

    Line3D(Point3D pt1, Point3D pt2): pt1(pt1), pt2(pt2)
    {
        this->pt1=pt1;
        this->pt2=pt2;
    }

    Point3D getPt1()
    {
        return pt1;
    }

    Point3D getPt2()
    {
        return pt2;
    }

    void setPt1(Point3D pt1)
    {
        this->pt1=pt1;
    }

    void setPt2 (Point3D pt2)
    {
        this->pt2=pt2;
    }
};
#endif // Line3D_h


Is there something I missed or need to change?
Last edited on
You didn't define Line3D()

There are a bunch of other problems, I lost my earlier reply - didn't copy it to clipboard before submitting.
You didn't define Line3D()

There are a bunch of other problems, I lost my earlier reply - didn't copy it to clipboard before submitting.


How do I define it?
Last edited on
Surely you know how to define a function?
1
2
3
Line2D();
Point2D();
Point3D();


These are function declarations - not definitions. If you declare a function you need to provide a function definition. Should these be:

1
2
3
Line2D() {}
Point2D() {}
Point3D() {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// function declaration:
void print( const Line2D& ) ;

// function definition, aka implementation:
void print( const Line2D& obj )
{
  // do something with obj
}

// forward declaration of a class:
class Line2D ;

// class definition:
class Line2D
{
  // members
};



The Line2D() is default constructor. There are three options.

1. The implementation, definition of the constructor is inside class definition:
1
2
3
4
5
6
7
8
9
class Line2D
{

  Line2D()
  {
    // something
  }

};


2. Ask compiler to create defaulted implementation:
1
2
3
4
5
6
class Line2D
{

  Line2D() = default ;

};

(This is possible only for some special members.)


3. Provide function definition separately:
1
2
3
4
5
6
7
8
9
10
11
12
class Line2D
{

  Line2D() ;

};


Line2D::Line2D()
{
  // something
}

Class definition should be in a header file so that it can be included where it is needed.

Separate implementation has to be in source (cpp) file that is compiled as separate translation unit and the resulting object file must be included in the linking of the executable.
Last edited on
It is the same issue as the other thread. If my answer in the other thread wasn't clear (totally possible), you should have spoken up.
https://cplusplus.com/forum/beginner/278029/
Last edited on
You may be wondering why it's complaining about Line2D::Line2D() when you never create a Line2D with the default constructor. The reason is line 18: vector<Line2D> line2DList; Vector<> calls the default constructor.

As an aside, it's confusing to put "list" in the name of a vector<> variable. Consider just using the plural: lines or lines2D
Actually, I think the issue is the Line3D constructor; it inherits Line2D but doesn't call its ctor.
The use of inheritance is questionable here. A Line3D is-a Line2D?

I think it's guaranteed that the default vector ctor will not construct any elements, but I can't find the specific paragraph, section that says this. I think this guarantee is made for: vector, forward_list, list, and deque.

Edit:
I found it,
An incomplete type T may be used when instantiating vector if the allocator satisfies the allocator completeness requirements (20.5.3.5.1). T shall be complete before any member of the resulting specialization of vector is referenced.

And the default allocator for vector does satisfy this:
23.10.9 The default allocator [default.allocator]
All specializations of the default allocator satisfy the allocator completeness requirements (20.5.3.5.1).
Last edited on
Ganado wrote:
The use of inheritance is questionable here. A Line3D is-a Line2D?

It gets even better:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Line2D
{
    Point2D pt1;
    Point2D pt2;
protected:
    double length;
};

class Line3D: public Line2D
{
    Point3D pt1;
    Point3D pt2;
protected:
    double length;
};

Every Line3D object thus contains six member variables:
1
2
3
4
5
6
    Point2D pt1;
    Point2D pt2;
    double  length;
    Point3D pt1;
    Point3D pt2;
    double  length;

Masked Singer, anyone?
Last edited on
1
2
3
4
5
double getLength()
    {
        double length=sqrt(pow(pt1.getX()-pt2.getX(),2)+pow(pt1.getY()-pt2.getY(),2)+pow(pt1.getZ()-pt2.getZ(),2));
        return length;
    }


length is also local to the getLength function.

On line 55 of Line2D it's referred to as distance:

1
2
cout<<"Line2D = ("<<l2d.pt1.getX()<<", "<<l2d.pt1.getY()<<"), "<<l2d.pt2.getX()<<", "<<l2d.pt2.getY()<<"); DISTANCE = "<<distance<<endl;
    


I would also question the use of this everywhere.

I agree with questioning the inheritance: it may be fine at the moment, there is only a length function; but what happens when a cross product is added to 3D classes? It violates Liscov Principle. Whatever functions there are in the derived class, functions with the same name should be able to be applied to the base class. Cross product makes no sense for 2D.

https://en.wikipedia.org/wiki/SOLID

Technically and perhaps pedantically: length should be magnitude as the identifier for the name of the variable.

I would not have variables x, y, z . Instead consider a std::vector<double > as a private: variable. Or std::array<double,3>. For the points pt1, pt2 , consider std::pair<Point3D, Point3D>

Edit: Maybe better for a Line, it could be std::vector<Point3D>. That way a lines can have many points, a Line segment is just a special case with 2 points.

The OP should make more use of member initialiser lists.
Last edited on
Its a performance hit if you are doing a LOT of things or real time graphics or something, but 2d is a subset of 3d, and your methods will work with zeroed out z components as if in 2d as long as you use common math sense for how you apply them, making special cases for 2d just a performance enhancement that you may not need. If you don't need it, maybe that will let you do what you need by just working in 3d?

eg the magnitude ;) in 3d is sqrt (x*x+y*y+z*z) and for 2d when z is 0, you have one extra multiply that has minimal costs...
Last edited on
Topic archived. No new replies allowed.