-1

I am trying to write a program that will allow the user to add items into a shopping cart and remove them. The assignment is to use the Bag class that is already provided by the instructor. ShoppingCart class will derive from Bag class. I am struggling with the inheritance and compiling it.

I am confused with the #include "Bag.cpp" at the end of Bag.h file (which is included by the professor). When I add #include "ShoppingCart.cpp" and such, it gives me different errors. But in this case, I am getting the following error. If I add those includes, I get redefinition errors.

I am also confused with which files to include for the compiling process on PuTTy.

I know it is a very long question, but I'll be glad if someone has some answers for me. In the main.cpp I haven't tried to invoke all the functions, basically I am not done with the main. Thank you.

P.S. The assignment requires my files to be separate as header/implementation files.

g++ -o main main.cpp Item.cpp
Undefined                       first referenced
 symbol                             in file
_ZN12ShoppingCartI4ItemEC1Ev        /var/tmp//cc52nA1n.o
_ZN12ShoppingCartI4ItemE3addES0_    /var/tmp//cc52nA1n.o
_Zeq4ItemS_                         /var/tmp//cc52nA1n.o
_ZN12ShoppingCartI4ItemE13getTotalPriceEv /var/tmp//cc52nA1n.o
ld: fatal: symbol referencing errors. No output written to main

BagInterface.h

#ifndef _BAG_INTERFACE
#define _BAG_INTERFACE

#include <vector>
using namespace std;

template<class ItemType>
class BagInterface
{
public:
   /** Gets the current number of entries in this bag.
    @return The integer number of entries currently in the bag. */
   virtual int getCurrentSize() const = 0;

   /** Sees whether this bag is empty.
    @return True if the bag is empty, or false if not. */
   virtual bool isEmpty() const = 0;

   /** Adds a new entry to this bag.
    @post  If successful, newEntry is stored in the bag and
       the count of items in the bag has increased by 1.
    @param newEntry  The object to be added as a new entry.
    @return  True if addition was successful, or false if not. */
   virtual bool add(const ItemType& newEntry) = 0;

   /** Removes one occurrence of a given entry from this bag,
       if possible.
    @post  If successful, anEntry has been removed from the bag
       and the count of items in the bag has decreased by 1.
    @param anEntry  The entry to be removed.
    @return  True if removal was successful, or false if not. */
   virtual bool remove(const ItemType& anEntry) = 0;

   /** Removes all entries from this bag.
    @post  Bag contains no items, and the count of items is 0. */
   virtual void clear() = 0;

   /** Counts the number of times a given entry appears in bag.
    @param anEntry  The entry to be counted.
    @return  The number of times anEntry appears in the bag. */
   virtual int getFrequencyOf(const ItemType& anEntry) const = 0;

   /** Tests whether this bag contains a given entry.
    @param anEntry  The entry to locate.
    @return  True if bag contains anEntry, or false otherwise. */
   virtual bool contains(const ItemType& anEntry) const = 0;

   /** Empties and then fills a given vector with all entries that
       are in this bag.
    @return  A vector containing all the entries in the bag. */
   virtual vector<ItemType> toVector() const = 0;
}; // end BagInterface

Bag.h

#ifndef _BAG
#define _BAG

#include "BagInterface.h"

template<class ItemType>
class Bag : public BagInterface<ItemType>
{
private:
    static const int DEFAULT_BAG_SIZE = 10;
    ItemType items[DEFAULT_BAG_SIZE]; // array of bag items
   int itemCount;                    // current count of bag items 
   int maxItems;                     // max capacity of the bag

   // Returns either the index of the element in the array items that
   // contains the given target or -1, if the array does not contain 
   // the target.
   int getIndexOf(const ItemType& target) const;   

public:
    Bag();
    int getCurrentSize() const;
    bool isEmpty() const;
    bool add(const ItemType& newEntry);
    bool remove(const ItemType& anEntry);
    void clear();
    bool contains(const ItemType& anEntry) const;
    int getFrequencyOf(const ItemType& anEntry) const;
    vector<ItemType> toVector() const; 
};  // end Bag

#include "Bag.cpp"

#endif

Bag.cpp

#include "Bag.h"
#include <cstddef>

template<class ItemType>
Bag<ItemType>::Bag() : itemCount(0), maxItems(DEFAULT_BAG_SIZE)
{
}  // end default constructor

template<class ItemType>
int Bag<ItemType>::getCurrentSize() const
{
    return itemCount;
}  // end getCurrentSize

template<class ItemType>
bool Bag<ItemType>::isEmpty() const
{
    return itemCount == 0;
}  // end isEmpty

template<class ItemType>
bool Bag<ItemType>::add(const ItemType& newEntry)
{
    bool hasRoomToAdd = (itemCount < maxItems);
    if (hasRoomToAdd)
    {
        items[itemCount] = newEntry;
        itemCount++;
    }  // end if

    return hasRoomToAdd;
}  // end add

template<class ItemType>
bool Bag<ItemType>::remove(const ItemType& anEntry)
{
   int locatedIndex = getIndexOf(anEntry);
    bool canRemoveItem = !isEmpty() && (locatedIndex > -1);
    if (canRemoveItem)
    {
        itemCount--;
        items[locatedIndex] = items[itemCount];
    }  // end if

    return canRemoveItem;
}  // end remove

template<class ItemType>
void Bag<ItemType>::clear()
{
    itemCount = 0;
}  // end clear

template<class ItemType>
int Bag<ItemType>::getFrequencyOf(const ItemType& anEntry) const
{
   int frequency = 0;
   int searchIndex = 0;
   while (searchIndex < itemCount)
   {
      if (items[searchIndex] == anEntry)
      {
         frequency++;
      }  // end if

      searchIndex++;
   }  // end while

   return frequency;
}  // end getFrequencyOf

template<class ItemType>
bool Bag<ItemType>::contains(const ItemType& anEntry) const
{
    return getIndexOf(anEntry) > -1;
}  // end contains

/* ALTERNATE 1
template<class ItemType>
bool Bag<ItemType>::contains(const ItemType& anEntry) const
{
    return getFrequencyOf(anEntry) > 0;
}  // end contains
*/
/* ALTERNATE 2 
template<class ItemType>
bool Bag<ItemType>::contains(const ItemType& anEntry) const
{
   bool found = false;
   for (int i = 0; !found && (i < itemCount); i++)
   {
      if (anEntry == items[i])
      {
         found = true;
      } // end if
   } // end for

   return found;
}  // end contains
*/

template<class ItemType>
vector<ItemType> Bag<ItemType>::toVector() const
{
    vector<ItemType> bagContents;
    for (int i = 0; i < itemCount; i++)
        bagContents.push_back(items[i]);
   return bagContents;
}  // end toVector

// private
template<class ItemType>
int Bag<ItemType>::getIndexOf(const ItemType& target) const
{
    bool found = false;
   int result = -1;
   int searchIndex = 0;
   // if the bag is empty, itemCount is zero, so loop is skipped
   while (!found && (searchIndex < itemCount))
   {
      if (items[searchIndex] == target)
      {
         found = true;
         result = searchIndex;
      } 
      else
      {
         searchIndex++;
      }  // end if
   }  // end while

   return result;
}  // end getIndexOf

ShoppingCart.h

#ifndef SHOPPINGCART_H
#define SHOPPINGCART_H

#include "Bag.h"
#include "Item.h"

#include <iostream>
#include <iomanip>

using namespace std;

template <class ItemType>
class ShoppingCart : public Bag<ItemType> {
private:
    double totalPrice;
public:
    ShoppingCart();
    double getTotalPrice();
    bool add(Item);
    bool remove(Item);

};


#endif //SHOPPINGCART_H

ShoppingCart.cpp

#include "ShoppingCart.h"

using namespace std;

// Default Constructor
template <class ItemType>
ShoppingCart<ItemType>::ShoppingCart() {
    totalPrice = 0;
}

template <class ItemType>
bool ShoppingCart<ItemType>::add(Item newItem) {

    bool added = Bag<ItemType>::add(newItem);

    totalPrice = totalPrice + (newItem.getQuantity() * newItem.getPrice());

    return added;
}

template <class ItemType>
bool ShoppingCart<ItemType>::remove(Item anItem) {

    bool removed = Bag<ItemType>::remove(anItem);

    totalPrice = totalPrice - (anItem.getQuantity() * anItem.getPrice());

    return removed;
}

template <class ItemType>
double ShoppingCart<ItemType>::getTotalPrice() {
    return totalPrice;
}

Item.h

#ifndef ITEM_H
#define ITEM_H

#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

class Item {
private:
    string name;
    double price;
    int quantity;
public:
    Item();
    Item(string n, double p, int q);
    // Setters
    void setName(string s);
    void setPrice(double p);
    void setQuantity(int q);
    // Getters
    string getName();
    double getPrice();
    int getQuantity();

    friend istream& operator >>(istream&, Item&);

};

bool operator ==(Item i1, Item i2);

Item operator <<(ostream& os, Item& source);

#endif //ITEM_H

Item.cpp

#include "Item.h"
#include <string>

using namespace std;

Item::Item() {

}

Item::Item(string n, double p, int q) {
    name = n;
    price = p;
    quantity = q;
}

// Setters
void Item::setName(string n) {
    name = n;
}
void Item::setPrice(double p) {
    price = p;
}
void Item::setQuantity(int q) {
    quantity = q;
}

// Getters
string Item::getName() {
    return name;
}
double Item::getPrice() {
    return price;
}
int Item::getQuantity() {
    return quantity;
}

// Definition of the friend function
istream& operator >>(istream& ins, Item& target)
{
    ins >> target.name >> target.price >> target.quantity;

    return ins;
}

// Definition of non-member functions
// << & == operator overloading
bool operator ==(Item& i1, Item& i2) {
    return (i1.getName()==i2.getName() && i1.getPrice()==i2.getPrice()
            && i1.getQuantity()==i2.getQuantity());

}

Item operator <<(ostream& os, Item& source) {
    os << source.getName() << " " << source.getPrice() << " " <<source.getQuantity() << endl;
}

main.cpp

#include "ShoppingCart.h"
#include "Item.h"

#include <iostream>
#include <iomanip>
#include <string>

using namespace std;


int main()
{
    cout << "Welcome to XXX SHOPPING CENTER" << endl;

    Item items[10];
    ShoppingCart<Item> cart;

    cout << "Enter the item you selected as the following order:\nname unitPrice quantity"
         << "\n(Name can not contain any space. Otherwise errors happen!)" << endl;

    cin >> items[0];

    cart.add(items[0]);

    cout << "The shopping cart contains: " << endl;

    cout << items[0];

    cout << "The total price of the order is " << cart.getTotalPrice() << endl;



    return 0;
}
ongelo
  • 185
  • 1
  • 3
  • 12
  • 3
    Possible duplicate of [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Miles Budnek Sep 20 '17 at 01:33
  • In this topic, it is suggested to use #include the implementation file at the end of the header file. However, when I do that, then I get redefinition errors for every single function that exists in the program. – ongelo Sep 20 '17 at 01:46
  • Odds are that's because your IDE sees compiles and links the cpp file. Change the name to something that is not cpp like impl so people (and the IDE) won't assume it is to be compiled. Another trick is don't separate the implementations from the class definitions you don't gain much from the separation in the case of a header. – user4581301 Sep 20 '17 at 02:18
  • It's hard to answer as you didn't provide Bag.cpp and the errors you are getting when you include it. Yes, you should likely include implementation such as Bag.cpp (although some use different extensions, such as .inc or .tpp) into the header, although sometimes you can only include them directly into the cpp file using the template class such as main.cpp. For g++ you should compile main.cpp and Item.cpp, the other 2 (Bag.cpp and ShoppingCart.cpp) should be included: https://stackoverflow.com/questions/30064686/making-and-compiling-c-projects-with-multiple-files – isp-zax Sep 20 '17 at 02:24
  • I compiled all the .cpp files separately into .o files. Does this mean they are error free individually, and I am having issues with inheriting/linking them together? – ongelo Sep 20 '17 at 02:39

2 Answers2

0

I think your problem is in == operator which is used in say getFrequencyOf() which tries to compare const Item& and fails since there is no appropriate == defined anywhere.

There are actually several problems with it, and without clear border between your code and assignment code, it's hard to be sure, but my best guess, that you should change

bool operator ==(Item i1, Item i2);

to

bool operator ==(const Item& i1, const Item& i2);

and then, implementation

// Definition of non-member functions
// << & == operator overloading
bool operator ==(Item& i1, Item& i2) {
    return (i1.getName()==i2.getName() && i1.getPrice()==i2.getPrice()
            && i1.getQuantity()==i2.getQuantity());

}

to const version

// Definition of non-member functions
// << & == operator overloading
bool operator ==(const Item& i1, const Item& i2) {
    return (i1.getName()==i2.getName() && i1.getPrice()==i2.getPrice()
            && i1.getQuantity()==i2.getQuantity());

}

which in turn wouldn't compile since you are using non-const getters, so all of those should be const. Change

// Getters
string getName();
double getPrice();
int getQuantity();

to

// Getters
const string& getName() const;
//string getName();
double getPrice() const;
int getQuantity() const;

and

// Getters
string Item::getName() {
    return name;
}
double Item::getPrice() {
    return price;
}
int Item::getQuantity() {
    return quantity;
}

to

// Getters
const string& Item::getName() const {
    return name;
}
double Item::getPrice() const {
    return price;
}
int Item::getQuantity() const {
    return quantity;
}

after which you should be able to compile and run it by including Bag.cpp as you professor did in the end of Bag.h and ShoppingCart.cpp in the end of ShoppingCart.h

I don't know a good way to share multiple file project, but I'm pretty sure including template implementations (Bag.cpp and ShoppingCart.cpp) will not change it. Your files compiled separately since main.cpp assumed that there might be == operator for const Item& somewhere. Item.cpp didn't have issues by itself. And linker told you that it can't find all functions it needed.

UPDATE:

The initial code didn't run successfully, but not because of the issues with templates or compilation. You had << wrong, with the same problem: wrong operator signature. So not getTotalPrice was dumping the core, but cout << Items[0] as it didn't return ostream&. Here is updated working REPL with the following changes:

//Item operator <<(ostream& os, Item& source);
std::ostream &operator <<(ostream& os, Item& source);

and

//Item operator <<(ostream& os, Item& source) {
//    os << source.getName() << " " << source.getPrice() << " " <<source.getQuantity() << endl;
//}
std::ostream &operator <<(ostream& os, Item& source) {
    return os << source.getName() << " " << source.getPrice() << " " <<source.getQuantity() << endl;
}

it outputs:

gcc version 4.6.3
Welcome to XXX SHOPPING CENTER
Enter the item you selected as the following order:
name unitPrice quantity
(Name can not contain any space. Otherwise errors happen!)
 ooo 2 3
The shopping cart contains: 
ooo 2 3
The total price of the order is 6
isp-zax
  • 3,833
  • 13
  • 21
  • It works great when I copy the code to one file and compile it. Then I managed to compile the multiple file version by entering "g++ -o main main.cpp Item.cpp". However when it comes to the getTotalPrice() function, it gives this error: "Segmentation fault (core dumped)". The function works perfectly when I compile it in only one file. I don't understand why. Thank you very much for the answer though, helped a lot! – ongelo Sep 20 '17 at 08:40
  • Your problem was bad << operator, not single/multiple files or templates. See my updated answer. – isp-zax Sep 20 '17 at 14:47
-1

I strongly suggest implementing all of your template functions in the header files after (not inside of) the class definition, and then #including all the .h files normally as needed (in other words, don't #include your .cpp files). EDIT: Re-read question. Sorry, my teacher always had us implement them in the same file if they were templates.

Axle12693
  • 46
  • 6
  • Thank you for the answer. Yes, unfortunately, they have to be in separate files, but I would rather get the assignment done. So I'll try your suggestion. Thank you. – ongelo Sep 20 '17 at 04:07