Hi, I am trying to compile and run an OOP Project but I keep getting 7 LNK2019 Errors everything looks to be defined and linked together. I do not understand why it is not running as it should. I tried running the OOP project in something else besides Visual Studio like CodeBlocks but I also keep getting "Undefined Reference to...". Any help on this topic would be greatly appreciated.
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include "orderedpair.h"
usingnamespace std;
usingnamespace cs_pairs;
int main() {
int num1, num2;
OrderedPair<int> myList[10];
srand(static_cast<unsigned>(time(0)));
cout << "default value: ";
myList[0].print();
cout << endl;
for (int i = 0; i < 10; i++) {
myList[i].setFirst(rand() % 50);
myList[i].setSecond(rand() % 50 + 50);
}
myList[2] = myList[0] + myList[1];
if (myList[0] < myList[1]) {
myList[0].print();
cout << " is less than ";
myList[1].print();
cout << endl;
}
for (int i = 0; i < 10; i++) {
myList[i].print();
cout << endl;
}
cout << "Enter two numbers to use in an OrderedPair. Make sure they are different numbers: ";
cin >> num1 >> num2;
OrderedPair<int> x;
/* use this before you've implemented the exception handling in the class:
*/
x.setFirst(num1);
x.setSecond(num2);
/* use this after you've implemented the exception handling in the class:
try {
x.setFirst(num1);
x.setSecond(num2);
} catch (OrderedPair::DuplicateMemberError e) {
x.setFirst(OrderedPair::DEFAULT_VALUE);
x.setSecond(OrderedPair::DEFAULT_VALUE);
}
*/
cout << "The resulting OrderedPair: ";
x.print();
cout << endl;
}
#include <iostream>
/* precondition for setFirst and setSecond: the values of first and second cannot be equal,
except when they are both equal to DEFAULT_VALUE.
*/
namespace cs_pairs
{
template <class T>
class OrderedPair
{
public:
// Use the first of the following two lines to make the non-templated version work.
// Switch to the second one when you begin converting to a templated version.
//static const int DEFAULT_VALUE = int();
//inline static const T DEFAULT_VALUE = T{};
staticconstint DEFAULT_VALUE;
typedef std::size_t size_type;
typedef T value_type;
OrderedPair(value_type newFirst = DEFAULT_VALUE, value_type newSecond = DEFAULT_VALUE);
void setFirst(value_type newFirst);
void setSecond(value_type newSecond);
value_type getFirst() const;
value_type getSecond() const;
OrderedPair operator+(const OrderedPair& right) const;
booloperator<(const OrderedPair& right) const;
void print() const;
class DuplicateMemberError
{
};
private:
value_type first;
value_type second;
};
// Leave the following const declaration commented out when you are testing the non-templated
// version. Uncomment it when you begin converting to a templated version.
// The templated version will require a template prefix as well as some minor edits to the code.
//template <class T>
//const int OrderedPair<T>::DEFAULT_VALUE = int();
}
When dealing with templates the implementation and declaration are usually both in the header file because both the implementation and declaration must be in the same compilation unit.
Whether this is what you intended, who knows. But what can be said for certain is:
1. What follows runs and produces a result which appears to make sense.
2. The concept of classes and data abstraction does not mean the real purpose should be obscured, especially when the added complexity of templates arises.
So all the typenames, even the namespaces seem to have got in the way of an otherwise simple job.
#include <iostream>
template <class T>
class OrderedPair
{
public:
staticconstint DEFAULT_VALUE = 0;
OrderedPair(T newFirst = DEFAULT_VALUE, T newSecond = DEFAULT_VALUE);
void setFirst(T newFirst);
void setSecond(T newSecond);
T getFirst() const;
T getSecond() const;
OrderedPair operator+(const OrderedPair& right) const;
booloperator<(const OrderedPair& right) const;
void print() const;
class DuplicateMemberError
{
};
private:
T first;
T second;
};
template <class T>
OrderedPair<T>::OrderedPair(T newFirst , T newSecond) {
setFirst(newFirst);
setSecond(newSecond);
}
template <class T>
void OrderedPair<T>::setFirst(T newFirst) {
first = newFirst;
}
template <class T>
void OrderedPair<T>::setSecond(T newSecond) {
second = newSecond;
}
template <class T>
T OrderedPair<T>::getFirst() const {
return first;
}
template <class T>
T OrderedPair<T>::getSecond() const {
return second;
}
template <class T>
OrderedPair<T> OrderedPair<T>::operator+(const OrderedPair<T>& right) const {
return OrderedPair(first + right.first, second + right.second);
}
template <class T>
bool OrderedPair<T>::operator<(const OrderedPair<T>& right) const {
return first + second < right.first + right.second;
}
template <class T>
void OrderedPair<T>::print() const {
std::cout << "(" << first << ", " << second << ")";
}
int main() {
int num1, num2;
OrderedPair<int> myList[10];
srand(static_cast<unsigned>(time(0)));
std::cout << "default value: ";
myList[0].print();
std::cout << std::endl;
for (int i = 0; i < 10; i++) {
myList[i].setFirst(rand() % 50);
myList[i].setSecond(rand() % 50 + 50);
}
myList[2] = myList[0] + myList[1];
if (myList[0] < myList[1]) {
myList[0].print();
std::cout << " is less than ";
myList[1].print();
std::cout << std::endl;
}
for (int i = 0; i < 10; i++) {
myList[i].print();
std::cout << std::endl;
}
std::cout << "Enter two numbers to use in an OrderedPair. Make sure they are different numbers: ";
std::cin >> num1 >> num2;
OrderedPair<int> x;
x.setFirst(num1);
x.setSecond(num2);
std::cout << "The resulting OrderedPair: ";
x.print();
std::cout << std::endl;
}
default value: (0, 0)
(37, 92)
(44, 53)
(81, 145)
(31, 96)
(10, 63)
(42, 84)
(41, 56)
(4, 79)
(43, 55)
(14, 65)
Enter two numbers to use in an OrderedPair. Make sure they are different numbers: 5 44
The resulting OrderedPair: (5, 44)
Program ended with exit code: 0
@AgainTry That is essentially what I was trying to do, but my requirements are having it in a namespace. I am going to tinker with it a bit and see if I can make it work with multiple files instead of one because I need to have it as multiple files.
It's obviously your call and a namespace for your class is useful.
My point about the namespaces etc is they sometimes get in the way of initial debugging with the bells and whistles to come later on in the process. ( The only namespace to avoid is "namespace std;" )
I must admit I couldn't see any advantage in the typenames though. :)
I am going to tinker with it a bit and see if I can make it work with multiple files instead of one because I need to have it as multiple files.
Templates are really designed to be in one file, but if you must have two then you will probably want to #include the source at the bottom of your header. #including the header in the source file will not do the trick.
PS I haven't tested it but you can easily split it back up into 3 files at the equivalent to my lines 30 and 71, adding the relevant #include's - none in the class header file required.
PPS Separate .h/.cpp file, as @jib writes, is however not a good idea.
So, you'll end up after all this with something like:
#include <iostream>
template <class T>
class OrderedPair
{
private:
T first;
T second;
public:
staticconstint DEFAULT_VALUE = 0;
OrderedPair(T newFirst = DEFAULT_VALUE, T newSecond = DEFAULT_VALUE) {
setFirst(newFirst);
setSecond(newSecond);
}
class DuplicateMemberError
{
};
void setFirst(T newFirst) {
first = newFirst;
}
void setSecond(T newSecond) {
second = newSecond;
}
T getFirst() const {
return first;
}
T getSecond() const {
return second;
}
OrderedPair<T> operator+(const OrderedPair<T>& right) const {
return OrderedPair(first + right.first, second + right.second);
}
booloperator<(const OrderedPair<T>& right) const {
return first + second < right.first + right.second;
}
void print() const {
std::cout << "(" << first << ", " << second << ")";
}
}; // <-- END OF CLASS (& HEADER FILE)
int main() {
int num1, num2;
OrderedPair<int> myList[10];
srand(static_cast<unsigned>(time(0)));
std::cout << "default value: ";
myList[0].print();
std::cout << std::endl;
for (int i = 0; i < 10; i++) {
myList[i].setFirst(rand() % 50);
myList[i].setSecond(rand() % 50 + 50);
}
myList[2] = myList[0] + myList[1];
if (myList[0] < myList[1]) {
myList[0].print();
std::cout << " is less than ";
myList[1].print();
std::cout << std::endl;
}
for (int i = 0; i < 10; i++) {
myList[i].print();
std::cout << std::endl;
}
std::cout << "Enter two numbers to use in an OrderedPair. Make sure they are different numbers: ";
std::cin >> num1 >> num2;
OrderedPair<int> x;
x.setFirst(num1);
x.setSecond(num2);
std::cout << "The resulting OrderedPair: ";
x.print();
std::cout << std::endl;
}
default value: (0, 0)
(3, 50) is less than (43, 70)
(3, 50)
(43, 70)
(46, 120)
(35, 72)
(6, 79)
(36, 82)
(28, 77)
(35, 53)
(22, 54)
(5, 72)
Enter two numbers to use in an OrderedPair. Make sure they are different numbers: 2 3
The resulting OrderedPair: (2, 3)
Program ended with exit code: 0
@AgainTry I also forgot to add having it in a namespace is necessary, do you think having it back to a multi file setup with a namespace will screw it up again?
@Depressed
I think we have crossed posts here so to make sure the 3 of us are clear.
A single file for the class in this template case is the best way to go.
So you'll have 2 files - the header, and main which #includes's your header.
BTW nominate a namespace - my comment related to the earlier exercise of initial debugging. Just make sure the no-namespace is working the way you want it, and then once any problems are solved move on, add the namespace and test.