-1

In the attached code, I can't get beyond the error function template has already been defined for every function in my class file.

I have been all through it and can't figure out where the functions are already defined anywhere. Note that this code was assembled while going through the chapters of a book, just trying to create functional code to start off with.

BagInterface.h:

/** @file BagInterface.h */
#ifndef BAG_INTERFACE_
#define BAG_INTERFACE_

#include <vector>

using std::vector;

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;

    /** See 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 addedd 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 the items is 0. */
    virtual void clear() = 0;

    /** Counts the number of times a given entry appears in this 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 top 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 copies of all the entries in this bag. */
    virtual vector<ItemType> toVector() const = 0;

    /** Destroys this bag and frees its assigned memory. */
    virtual ~BagInterface() { }
}; // end BagInterface
#endif

ArrayBag.h:

/** @file ArrayBag.h */
#ifndef ARRAY_BAG_
#define ARRAY_BAG_

#include "BagInterface.h"

template<class ItemType>
class ArrayBag : public BagInterface<ItemType>
{
private:
    static const int DEFAULT_CAPACITY = 6;
    ItemType items[DEFAULT_CAPACITY];
    int itemCount;
    int maxItems;
    int getIndexOf(const ItemType& target, int searchIndex) const;
    int countFrequency(const ItemType& target, int searchIndex) const;

public:
    ArrayBag();
    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;
};

#include "ArrayBag.cpp"
#endif

ArrayBag.cpp:

#include "ArrayBag.h"


template<class ItemType>
ArrayBag<ItemType>::ArrayBag() : itemCount(0), maxItems(DEFAULT_CAPACITY)
{

}

template<class ItemType>
int ArrayBag<ItemType>::getIndexOf(const ItemType& target, int searchIndex) const
{
    int result = -1;

    if (searchIndex < itemCount)
    {
        if (items[searchIndex] == target)
        {
            result = searchIndex;
        }
        else
        {
            result = getIndexOf(target, searchIndex + 1);
        }
    }
    return result;
}

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

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

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

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

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

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

template<class ItemType>
int ArrayBag<ItemType>::getFrequencyOf(const ItemType& anEntry) const
{
    return countFrequency(anEntry, 0);
}

template<class ItemType>
int ArrayBag<ItemType>::countFrequency(const ItemType& target, int searchIndex) const
{
    int frequency = 0;
    if (searchIndex < itemCount)
    {
        if (items[searchIndex] == target)
        {
            frequency = 1 + countFrequency(target, searchIndex + 1);
        }
        else
        {
            frequency = countFrequency(target, searchIndex + 1);
        }
    }
    return frequency;
}

template<class ItemType>
bool ArrayBag<ItemType>::contains(const ItemType& anEntry) const
{
    bool found = false;
    int curIndex = 0;
    while (!found && (curIndex < itemCount))
    {
        if (anEntry == items[curIndex])
            found = true;
        else
            curIndex++;
    }
    return found;
}

Bag.cpp:

#include <iostream>
#include <string>
#include "ArrayBag.h"

using std::cout;
using std::endl;

void displayBag(ArrayBag<std::string>& bag)
{
    cout << "The bag contains " << bag.getCurrentSize() << " items:" << endl;
    vector<std::string> bagItems = bag.toVector();

    int numberOfEntries = (int)bagItems.size();
    for (int i = 0; i < numberOfEntries; i++)
    {
        cout << bagItems[i] << " ";
    }
    cout << endl << endl;
}

void bagTester(ArrayBag<std::string>& bag)
{
    cout << "isEmpty: returns " << bag.isEmpty() << "; should be 1 (true)" << endl;
    displayBag(bag);

    std::string items[] = { "one","two", "three", "four", "five", "one" };
    cout << "Add 6 items to the bag: " << endl;
    for (int i = 0; i < 6; i++)
    {
        bag.add(items[i]);
    }

    displayBag(bag);
    cout << "isEmpty: returns " << bag.isEmpty() << "; should be 0 (false)" << endl;
    cout << "getCurrentSize: returns " << bag.getCurrentSize() << "; should be 6" << endl;
    cout << "Try to add another entry:  add(\"extra\") returns " << bag.add("extra") << endl;
}

int main()
{
    ArrayBag<std::string> bag;
    cout << "Testing the Array-Based Bag:" << endl;
    cout << "The initial bag is empty." << endl;
    bagTester(bag);
    cout << "All done!" << endl;
    std::cin.get();

    return 0;
}
stefanobaghino
  • 11,253
  • 4
  • 35
  • 63
davejr72
  • 13
  • 6
  • 1
    When asking questions about build-errors, always include them in the question body. Please copy (as text) the *full* and *complete* output, and paste it (without modifications except formatting as code with indentation) into your question. Then pointer where in your code the errors, for example by comments. – Some programmer dude Jan 28 '18 at 20:53
  • I'm *guessing* that these are linker errors. If so the cause is including ArrayBag.cpp in ArrayBag.h, don't do that. Throw away ArrayBag.cpp and move all the definitions to ArrayBag.h. Obviously in a header file function definitions must be inline. – john Jan 28 '18 at 20:55
  • 1
    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) – Henri Menke Jan 28 '18 at 20:56
  • Just another ***guess***, but do you *build* with the `ArrayBag.cpp` source file as well as include it? I.e. do you compile `ArrayBag.cpp` into an object file that you then link with? – Some programmer dude Jan 28 '18 at 20:59
  • Please post (at least a few lines) of the exact error messages. – Johan Lundberg Jan 28 '18 at 21:40

3 Answers3

-1

I've reproduced your problem and understand what's going on. The unusual construct with include ArrayBag.cpp works fine, as long as you don't compile it, and just use it "as a header". But it's content must appear only once in each translation unit. That's why we have include guards: Any file you #include should have include guards, and ArrayBag.cpp dosn't. Here's how it goes:

  1. You compile Arraybag.cpp, which

  2. includes the declarations in ArrayBag.h Fine, but

  3. ArrayBag.h then includes Arraybag.cpp, which does not have its own include guard

  4. The definitions are parsed, and after the end of Arraybag.cpp, parsing continues back at the end of ArrayBag.h .

  5. We are now done with the first line of the compilation of ArrayBag.cpp, but we are still parsing it: and we go through the rest of ArrayBag.cpp again.

You can still compile ArrayBag.cpp, for the sake of it:

ArrayBag.cpp

// would just be
#include "ArrayBagDefinitions.h"

And ArrayBagDefinitions.h would be your old ArrayBag.cpp file, but with include guards. Your code then compiles fine with vs2017.

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • It's interesting to me that I'm receiving down votes but you have effectively covered the exact same things in your answer. There *are* benefits for putting the definitions in a separate file - just not to the compiler. It's easier to digest an interface without the implementation if someone came looking to see what a class has to provide. In fact, I'd even assert that for things outside the standard library, it's more common than not to use this pattern. After all, the book he's referencing is using it. – Austin Brunkhorst Jan 28 '18 at 22:24
-1

Can you please show the makefile, environment, compiler ? I've compiled it whitout problems, I'm using qt creator IDE under windows(mingw32), and the makefile is created automaticly by qmake, so, I supouse that your problem may be in the makefile, I know that it is a comment, but due to my reputation I am not able to make comments, so, after you show the makefile I will can help you.

please avoid mark as negative this answer, I am tryining of help, and increase my reputation. if it answer is not contructive please, let me know and I will remove it.

the output of for code is :

output

andress hoyo
  • 161
  • 1
  • 14
  • That is strange. I am using Visual Studio 2017, and I cannot get it to compile without errors regardless of what suggestions I try. I am unfamiliar with what a makefile is – davejr72 Jan 28 '18 at 22:28
  • I am using Qt ide and it don't show problemes to compile. – andress hoyo Jan 28 '18 at 22:33
  • 1
    I down voted your answer because it does not answer the question, it only states that it works for you in your environment. I understand the limitations with respect to your reputation, unfortunately it's not a reason to represent a comment as an answer. – Austin Brunkhorst Jan 28 '18 at 22:35
  • ok I get your point, but can you say me, as feedback, how can I help he if a cannot reproduce the same error, if under my environment it doesn't fall, as you , I just want help to he, but due to my reputation I am so limited. – andress hoyo Jan 28 '18 at 22:47
  • 2
    My feedback is to save your answers for when you know that they can be complete and meaningful. It's certainly helpful to collaborate with OP, but in the event that someone revisits this question, this answer isn't useful to them in a general or specifically to OP - it should be done in the comments. You have endless opportunity to grow reputation. Don't get discouraged! – Austin Brunkhorst Jan 28 '18 at 22:50
  • thanks, I will take care for next opportunities, BTW that means OP, I 'm not english native. – andress hoyo Jan 28 '18 at 23:23
  • No worries! OP means **O**riginal **P**oster, the person who asked the question. – Austin Brunkhorst Jan 28 '18 at 23:31
-2

In your book, did they mention not to compile ArrayBag.cpp?

In C++, the compiler generates code when a template class is instantiated. Our trusty friend can't do this without a definition. For this reason, the definition needs to be available at compile time (there's more reasons why). For readability, you'll often see people separate the declaration of a template class from the definition.

As the book has shown you, we can take advantage of #include to supply the definition to the compiler while still separating the two.

The caveat here is that you cannot compile the files you used to separate the definition. Depending on whether ArrayBag.cpp is compiled before ArrayBag.h is included somewhere else, the compiler will have a hizzy fit when he finds out that you already tried to define ArrayBag.

Alternatively, you can add an include guard in ArrayBag.cpp, which will prevent the compiler from attempting define ArrayBag again during compilation of ArrayBag.cpp, but I don't advise this. It will add unnecessary time during compilation. After all, we're abusing the simplicity of #include here to mitigate a pain point in the requirements of the compiler.


If you're using Visual Studio, you can treat a .cpp file as a header file by right clicking on the file in the solution explorer > Properties > General > Excluded From Build: Yes

Visual Studio Exclude From Build

Austin Brunkhorst
  • 20,704
  • 6
  • 47
  • 61
  • 1
    The include guards should protect against the include loop. And, templates methods are inline, so it's fine to include the definitions repeatedly. – Johan Lundberg Jan 28 '18 at 20:57
  • I jumped the gun on that one. I've got a better answer for ya! – Austin Brunkhorst Jan 28 '18 at 21:31
  • Still wrong as far as I understand it. `ArrayBag` is a template class. Class declarations are allowed to be repeated. Members of templates are inline. Everything found in `ArrayBag.cpp` could just as well be in the header. – Johan Lundberg Jan 28 '18 at 21:36
  • I've already test this in MSVC 15. I get the same error when compiling `ArrayBag.cpp`, versus excluding it. It has nothing to do with how the class is treated (inline or not), it's that there are two passes in which `ArrayBag` will try to be defined - when `ArrayBag.cpp` is compiled and when `ArrayBag.cpp` is included in `ArrayBag.h`. – Austin Brunkhorst Jan 28 '18 at 21:37
  • @JohanLundberg `Class declarations are allowed to be repeated` - since when? I think you mean class declarations are allowed to be forward declared. Also, inline or not, I don't think you're correct in saying that they're fine to be defined repeatedly. The include guards are what prevent that from happening. – Austin Brunkhorst Jan 28 '18 at 21:43
  • Class definitions are allowed to be repeated with respect to linkage. You can write `class C{};` and include that file in several translation units, link to the same program. Unlike for example non-inline function definitions. Also, you write "ArrayBag.cpp is compiled before ArrayBag.h". There is no such thing, as ArrayBag.h ends with including the cpp, and the cpp starts with including the .h file. What is the error you said you were able to reproduce? – Johan Lundberg Jan 28 '18 at 21:45
  • I understand what you mean now. In that case yes, however, one translation unit cannot attempt to define a template class that's already been defined, which is the problem OP is having by compiling `ArrayBag.cpp`, since it doesn't have an include guard. – Austin Brunkhorst Jan 28 '18 at 21:48
  • @AustinBrunkhorst - I tried excluding it from the build and it didn't work. I also removed #include"arrayBag.cpp from arrayBag.h. Now I get a couple of errors that I definitely don't understand! Lol. "LNK2019" ...and... "LNK1120" – davejr72 Jan 28 '18 at 21:59
  • When you excluded `ArrayBag.cpp` from the build, what errors did you get? – Austin Brunkhorst Jan 28 '18 at 22:00
  • LNK2019: "unresolved external symbol "public: __thiscall ArrayBag,class std::allocator > >::ArrayBag,class std::allocator > >(void)" (??0?$ArrayBag@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@QAE@XZ) referenced in function _main Bag C:\Users\Default.DESKTOP-RGI5TQV\source\repos\Assignment_testing\Bag\Bag\Bag.obj (Line 1)" – davejr72 Jan 28 '18 at 22:00
  • That's a linker error, which indicates that the compiler didn't find a definition, which makes sense if you remove the include in `ArrayBag.h` – Austin Brunkhorst Jan 28 '18 at 22:01
  • LNK1120: "unresolved externals Bag C:\Users\Default.DESKTOP-RGI5TQV\source\repos\Assignment_testing\Bag\Debug\Bag.exe (Line 1)" – davejr72 Jan 28 '18 at 22:01
  • That seems unrelated to the code you showed us, as `Bag` isn't referenced anywhere. I was able to successfully compile all 3 files you provided us by simply excluding `ArrayBag.cpp` from the build. – Austin Brunkhorst Jan 28 '18 at 22:05
  • `Bag` is the source file with `main{}` – davejr72 Jan 28 '18 at 22:31
  • Try rebuilding the solution. – Austin Brunkhorst Jan 28 '18 at 22:36
  • I appreciate everyone's help, but nothing seems to be solving this for me. I am becoming more confused, rather than developing a better understanding. I think I may just have to start fresh and take a different approach. – davejr72 Jan 28 '18 at 22:51
  • I'd start by creating a fresh project in Visual Studio, adding the 3 files you gave us, and changing the properties of `ArrayBag.cpp` to be excluded from the build. This is how I reproduced your errors and got it to compile cleanly. – Austin Brunkhorst Jan 28 '18 at 23:01
  • 1
    @AustinBrunkhorst - The strangest thing... I tried your last comment 3 times last night, starting a fresh project every time (stored in different locations.) Wouldn't work. I just got out of work and tried it once more before starting fresh and it worked! I don't know what the deal was, but thank you. – davejr72 Jan 29 '18 at 22:49
  • It's hard to say without knowing what you had going on in your environment, the steps you took in what order, etc. but it happens! When I come across strange behavior like this, I start with nothing and add minor things with expected behavior. This way, it's easy to see what change causes the behavior. If this did resolve your issues, I suggest marking as an answer and being on your merry way! – Austin Brunkhorst Jan 29 '18 at 23:22