Class Inheritance & Template Class C++

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.

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

1
2
3
4
5
6
7
8
    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



Bag.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#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

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


Bag.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
 #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 


ShoppingCart.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
 #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

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
    #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

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
   #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

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
   #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;
    }
Last edited on
Template definitions must be visible from their point of instantiation. This is because function templates are not functions, and class templates are not classes, but are merely instructions for creating them.

Usually this means that template code is written in the header (rather: in the same file it's declared in, or entirely inline). If you want to separate implementation from the interface, then you may declare the templates in a header file and define the templates in another file, which is then included at the bottom of the header file. In my opinion, this workaround is usually a waste of time.

It is also possible to explicitly instantiate templates, which avoids the issue for a few selected types, but that feature isn't very often useful in practice. Look up "template instantiation" and "extern templates".

See:
https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
https://stackoverflow.com/questions/115703/storing-c-template-function-definitions-in-a-cpp-file
Last edited on
I am one who likes to put template class definition code in a separate file in many cases. While I understand @mbozzi's suggestion that it is usually a waste of time, I politely and mildly disagree. I like the separation of header information from instantiation instructions, and find that this organization mimics header/class separation for normal classes.

However, I do not use .cpp to represent the template instantiation code. I use another extension (usually .tcpp) to represent my template instantiation instruction file. The .tcpp file is included at the bottom of the header file. This makes sure that I do not try to compile my template code in a file by itself. A .cpp file should never be included by another .cpp file, and changing the extension of the template "source" file preserves this.

This method allows the user to see the header for the template class without getting bogged down in the details of the template class function code, but the compiler can still see the code when it is needed.

I have even had instances where I have a normal class with template member functions. In these cases I have a .h, a .cpp and a .tcpp file for the same class. This is rare, and it's really not as confusing as it sounds.

There are probably more people on this forum who put all of the template code into the header file than separate it into 2 files like I do. I get it. I'm not looking for a religious war here, and I'm not saying that my way is better. But if you like the header/source pattern and want to maintain it for template classes, this is how you can do it. Just don't name the source file .cpp.

Unfortunately, for the @OP, the Bag.cpp file was given as part of the assignment. This is an extremely disappointing name for that file. (If I were reviewing this code for work, I would insist that the file be renamed for these very reasons.) So, just realize that Bag.cpp is not really a .cpp file and do not pattern any of your code after line 32 in Bag.h.
Also if you #include the implementation file at the bottom of the header you shouldn't need to #include the header in the implementation file and you need to insure that the implementation file is not compiled as part of your project.

Topic archived. No new replies allowed.