功能模板已经定义了?

时间:2018-01-28 20:46:36

标签: c++

在附加的代码中,对于我的类文件中的每个函数,我都无法超越错误function template has already been defined

我已经全力以赴,无法弄清楚功能已在任何地方定义。请注意,这段代码是在阅读一本书的章节时编写的,只是尝试创建功能代码以便开始。

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;
}

3 个答案:

答案 0 :(得分:-1)

我已经复制了你的问题,并了解发生了什么。具有include ArrayBag.cpp的不寻常构造工作正常,只要您不编译它,并且只是将其用作“标题”。但它的内容必须在每个翻译单元中只出现一次。这就是为什么我们要包含警卫:你#include应该包含警卫的任何文件,以及ArrayBag.cpp dos。这是怎么回事:

  1. 编译Arraybag.cpp,

  2. 包含ArrayBag.h中的声明,但

  3. 然后ArrayBag.h包含Arraybag.cpp,它没有自己的包含保护

  4. 解析 definitions ,并在Arraybag.cpp结束后,解析继续在ArrayBag.h的末尾。

  5. 我们现在完成了ArrayBag.cpp编译的第一行,但我们仍在解析它:我们再次遍历ArrayBag.cpp 的其余部分

  6. 为了它,您仍然可以编译ArrayBag.cpp

    ArrayBag.cpp

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

    并且ArrayBagDefinitions.h将是您的旧ArrayBag.cpp文件,但包含防护。然后你的代码可以用vs2017编译好。

答案 1 :(得分:-1)

你能否展示一下makefile,环境,编译器?我编译了whitout问题,我在windows下使用qt creator IDE(mingw32),并且makefile是由qmake自动创建的,所以,我支持你的问题可能在makefile中,我知道这是一个评论,但由于我的声誉,我无法发表评论,因此,在您显示makefile后,我会帮助您。

请避免标记为否定此答案,我正在尝试帮助,并提高我的声誉。如果答案不具有破坏性,请告诉我,我会删除它。

代码的输出是:

output

答案 2 :(得分:-2)

在你的书中,他们是否提到编译ArrayBag.cpp

在C ++中,编译器在模板类实例化时生成代码。我们可靠的朋友没有定义就无法做到这一点。因此,定义需要在编译时可用( there's more reasons why )。为了便于阅读,您经常会看到人们将declaration模板类与definition分开。

正如本书向您展示的那样,我们可以利用#include向编译器提供定义,同时将两者分开。

这里需要注意的是,您无法编译用于分隔定义的文件。根据在ArrayBag.cpp被包含在其他地方之前是否编译ArrayBag.h,当编译器发现您已经尝试定义ArrayBag时,编译器会有一种适应性。

或者,您可以在ArrayBag.cpp中添加包含保护,这将阻止编译器在编译ArrayBag期间再次尝试定义ArrayBag.cpp,但我不建议这样做。它会在编译期间增加不必要的时间。毕竟,我们在这里滥用#include的简单性来缓解编译器要求中的痛点。

如果您正在使用Visual Studio,则可以通过右键单击解决方案资源管理器中的文件&gt; 属性.cpp文件视为头文件&gt; 一般&gt; 从构建中排除:

Visual Studio Exclude From Build