LINKER ERRORS

How do I get rid of the linker errors. I believe the errors come from lines 17 to 31 of the test program.

Here is the output:
1>------ Build started: Project: Chapter 5, Configuration: Debug Win32 ------
1>ftest93.obj : error LNK2019: unresolved external symbol "bool __cdecl CPPBook::operator<(class CPPBook::Fraction const &,class CPPBook::Fraction const &)" (??MCPPBook@@YA_NABVFraction@0@0@Z) referenced in function _main
1>C:\Users\Dexter\OneDrive\Object-Oriented Programming\Chapter 5\Debug\Chapter 5.exe : fatal error LNK1120: 1 unresolved externals
1>Done building project "Chapter 5.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


Here is the coding in the header file, "frac93.hpp". The preprocessor directives were commented out.
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
/* The following code example is taken from the book
 * "Object-Oriented Programming in C++"
 * by Nicolai M. Josuttis, Wiley, 2002
 *
 * (C) Copyright Nicolai M. Josuttis 2002.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */

//#ifndef FRACTION.HPP
//#define FRACTION.HPP

// include standard header files
#include <iostream>

// **** BEGIN namespace CPPBook ********************************
namespace CPPBook {

class Fraction {
  protected:
    int numer;
    int denom;

  public:
    /* error class
     */
    class DenomIsZero {
    };

    /* default constructor, one-, and two-parameter constructor
     */
    Fraction(int = 0, int = 1);

    /* multiplication
     * - global friend function, so that an automatic
     *     type conversion of the first operand is possible
     */
    friend Fraction operator * (const Fraction&, const Fraction&);

    /* multiplicative assignment
     * - new: virtual
     */
    virtual const Fraction& operator *= (const Fraction&);

    /* comparison
     * - global friend function, so that an automatic
     *     type conversion of the first operand is possible
     */
    friend bool operator < (const Fraction&, const Fraction&);

    /* output to and input from a stream
     * - new: virtual
     */
    virtual void printOn(std::ostream&) const;
    virtual void scanFrom(std::istream&);

    /* type conversion to double
     * - new: virtual
     */
    virtual double toDouble() const;

    // new: virtual destructor (without instructions)
    virtual ~Fraction() {
    }
};

/* operator *
 * - global friend function
 * - inline defined
 */

inline Fraction operator * (const Fraction& a, const Fraction& b)
{
	/* simply multiply numerator and denominator
    * - this saves time
   */
   return Fraction(a.numer * b.numer, a.denom * b.denom);
}

/* comparison operator <
*	global friend function
*/
/*
bool operator < (const Fraction& a, const Fraction& b)
{
	// since the denonminator cannot be negative, the following is sufficient
	return a.numer * b.denom < b.numer * a.denom;
}
*/

/* standard output operator
 * - overloaded globally and inline defined
 */
inline
std::ostream& operator << (std::ostream& strm, const Fraction& f)
{
    f.printOn(strm);    // call member function for output
    return strm;        // return stream for chaining
}

/* standard input operator
 * - overloaded globally and inline defined
 */
inline
std::istream& operator >> (std::istream& strm, Fraction& f)
{
    f.scanFrom(strm);   // call member function for input
    return strm;        // return stream for chaining
}

} // **** END namespace CPPBook ********************************

//#endif  // FRACTION_HPP 



Here is the implementation file, "frac93.cpp".
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
// inherit/frac93.cpp
#include "frac93.hpp"

#include <cstdlib>

// ****** BEGIN namespace CPPBook ******
namespace CPPBook {

	// Constructor
	Fraction::Fraction(int n, int d)
	{
		if (d == 0){
			std::cerr << "error:  deniminator is 0" << std::endl;
			std::exit(EXIT_FAILURE);
		}
		if (d < 0){
			numer = -n;
			denom = -d;
		}
		else{
			numer = n;
			denom = d;
		}
	}

	// Multiplication Assignment
	const Fraction& Fraction::operator*= (const Fraction& f)
	{
		*this = *this * f;

		// object (first operand) is returned
		return *this;
	}

	// printOn
	void Fraction::printOn(std::ostream& strm) const
	{
		strm << numer << '/' << denom;
	}

	// scanFrom
	void Fraction::scanFrom(std::istream& strm)
	{
		int n, d;
		
		// read numerator
		strm >> n;

		// read optional separator '/' and denominator
		if (strm.peek() == '/'){
			strm.get();
			strm >> d;
		}
		else{
			d = 1;
		}

		// read error?
		if (!strm) {
			return;
		}

		// denominator equals zero?
		if (d == 0){
			// set failbit
			strm.clear(strm.rdstate() | std::ios::failbit);
			return;
		}
		 
		// assign read values
		if (d < 0){
			numer = -n;
			denom = -d;
		}
		else{
			numer = n;
			denom = d;
		}
	}

	// toDouble
	double Fraction::toDouble() const
	{
		return double(numer) / double(denom);
	}

}


Here is the test file, "ftest93.cpp". This is where I think the errors are, lines 27 to 31.
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
// ftest93.cpp
// include standard header files
#include <iostream>
#include <cstdlib>

// include header file for the classes that are being used
#include "frac93.hpp"

int main()
{
	const CPPBook::Fraction a(7, 3);			// declare fraction constant a
	CPPBook::Fraction x;							// declare fraction variable x

	std::cout << a << std::endl;				// output fraction a

	// read fraction x
	std::cout << "enter fraction (numer/denom): ";
	if (!(std::cin >> x)){
		// input error: exit program with error status
		std::cerr << "Error during input of fraction" << std::endl;
		system("pause");
		return EXIT_FAILURE;
	}
	std::cout << "Input was: " << x << std::endl;
	
	// as long as x is less than 1000
	while (x < 1000){
		// multiply x by a and output result
		x = x * a;
		std::cout << x << std::endl;
	}

	system("pause");
	return 0;
}

Last edited on
You need to define bool operator < (const Fraction&, const Fraction&). You currently have it commented out.
Last edited on
Thanks, I uncommented the comparison operator but still have linker errors.

Here is the output:
1
2
3
4
5
6
7
8
9
1>Generating Code...
1>Compiling...
1>ftest93.cpp
1>Generating Code...
1>ftest93.obj : error LNK2005: "bool __cdecl CPPBook::operator<(class CPPBook::Fraction const &,class CPPBook::Fraction const &)" (??MCPPBook@@YA_NABVFraction@0@0@Z) already defined in frac93.obj
1>C:\Users\Dexter\OneDrive\Object-Oriented Programming\Chapter 5\Debug\Chapter 5.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Done building project "Chapter 5.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Not sure if this is the exact problem, but why did you comment out your include guards?

Uncomment lines 12, 13, and and 115, but replace the "." (period) with an "_" (underscore)
i.e. FRACTION_HPP not FRACTION.HPP (periods can't be in macro names. The point of a header gaurd is that the name is unique, it can be BLAHA234328u7DHFKD if you wanted...)
Last edited on
Make operator < "inline" like the others.
Also, the symbol you define for your include guards can't have a period. Change the period to an underscore.
Last edited on
Thanks Ganado, and tpb. I used underscore instead of a period, and the preprocessor directives work.

tpb,

Why would making the operator < inline get rid of the linker errors?

Why would making the operator < inline get rid of the linker errors?

Because if it's not an inline function then it's being defined in two compilation units: ftest93.cpp and frac93.cpp. When the object files are linked, the linker finds two copies of the same function. If it's inline then it's inlined in the code where it's used and not defined anywhere.

BTW, the missing include guards were not part of the problem since the header was only included once in each compilation unit, and compilation units are separately compiled. But it's good to have them anyway.
Last edited on
Yeah the problem was the inline. What I guessed was a bit misleading since I didn't notice the lack of inline.

If you didn't want to use the inline keyword, you'd declare the function like this in your header:
bool operator < (const Fraction& a, const Fraction& b);

And define it like this in the implementation file:
1
2
3
4
5
bool operator < (const Fraction& a, const Fraction& b)
{
	// since the denonminator cannot be negative, the following is sufficient
	return a.numer * b.denom < b.numer * a.denom;
}
Ganado,

Thank you very much. I am beginning to understand what I did better. My understanding is that even though the inline keyword is used, the complier decides whether the function is inline or not. On the other hand, if you don't declare the function inline, the complier will make it inline if determines so. In the event the function is not an inline function, then it would be best to define the comparison operator in the implementation file.
Topic archived. No new replies allowed.