__thiscall 已在 .obj 中定义

__thiscall already defined in .obj

我收到链接器错误,我似乎无法找到根本原因,已检查是否包含 .cpp 文件并阅读其他论坛。

错误是:

1>------ Build started: Project: Penguin_RPG, Configuration: Debug Win32 ------
1>  main.cpp
1>c:\users\adam\documents\visual studio 2013\projects\penguin_rpg\penguin_rpg\dialogue.hpp(53): warning C4018: '<=' : signed/unsigned mismatch
1>c:\users\adam\documents\visual studio 2013\projects\penguin_rpg\penguin_rpg\main.cpp(39): warning C4244: 'argument' : conversion from 'time_t' to 'unsigned int', possible loss of data
1>main.obj : error LNK2005: "public: __thiscall Area::Area(class Dialogue,class Inventory,class std::vector<class Creature *,class std::allocator<class Creature *> >)" (??0Area@@QAE@VDialogue@@VInventory@@V?$vector@PAVCreature@@V?$allocator@PAVCreature@@@std@@@std@@@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: __thiscall Creature::Creature(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int,int,int,int,double,unsigned int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0Creature@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@HHHHNI0@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: __thiscall Creature::Creature(void)" (??0Creature@@QAE@XZ) already defined in atlas.obj
1>main.obj : error LNK2005: "public: __thiscall Dialogue::Dialogue(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >)" (??0Dialogue@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@2@@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: __thiscall Item::Item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0Item@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: __thiscall Weapon::Weapon(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,unsigned int,double)" (??0Weapon@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0IN@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: int __thiscall Dialogue::activate(void)" (?activate@Dialogue@@QAEHXZ) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::add_armour(class Armour *,int)" (?add_armour@Inventory@@QAEXPAVArmour@@H@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::add_item(class Item *,int)" (?add_item@Inventory@@QAEXPAVItem@@H@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::add_weapon(class Weapon *,int)" (?add_weapon@Inventory@@QAEXPAVWeapon@@H@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::clear(void)" (?clear@Inventory@@QAEXXZ) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Creature::equipArmour(class Armour *)" (?equipArmour@Creature@@QAEXPAVArmour@@@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Creature::equipWeapon(class Weapon *)" (?equipWeapon@Creature@@QAEXPAVWeapon@@@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: unsigned int __thiscall Creature::expToLevel(unsigned int)" (?expToLevel@Creature@@QAEII@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: bool __thiscall Creature::levelUp(void)" (?levelUp@Creature@@QAE_NXZ) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::merge(class Inventory *)" (?merge@Inventory@@QAEXPAV1@@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::print(bool)" (?print@Inventory@@QAEX_N@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: int __thiscall Inventory::print_armours(bool)" (?print_armours@Inventory@@QAEH_N@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: int __thiscall Inventory::print_items(bool)" (?print_items@Inventory@@QAEH_N@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: int __thiscall Inventory::print_weapons(bool)" (?print_weapons@Inventory@@QAEH_N@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::remove_armour(class Armour *,int)" (?remove_armour@Inventory@@QAEXPAVArmour@@H@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::remove_item(class Item *,int)" (?remove_item@Inventory@@QAEXPAVItem@@H@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Inventory::remove_weapon(class Weapon *,int)" (?remove_weapon@Inventory@@QAEXPAVWeapon@@H@Z) already defined in atlas.obj
1>main.obj : error LNK2005: "public: void __thiscall Area::search(class Creature &)" (?search@Area@@QAEXAAVCreature@@@Z) already defined in atlas.obj
1>C:\Users\Adam\Documents\Visual Studio 2013\Projects\Penguin_RPG\Debug\Penguin_RPG.exe : fatal error LNK1169: one or more multiply defined symbols found
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

atlas.hpp:

#pragma once
#ifndef ATLAS_HPP
#define ATLAS_HPP

#include <vector>

#include "creature.hpp"
#include "item.hpp"
#include "weapon.hpp"
#include "armour.hpp"
#include "area.hpp"

//atlas building functions. Atlases contain vectors of
//game data that is not modified. Could be replaced with
//functions that read from config files.

void buildatlas_creature(std::vector<Creature>& atlas);
void buildatlas_item(std::vector<Item>& atlas);
void buildatlas_weapon(std::vector<Weapon>& atlas);
void buildatlas_armours(std::vector<Armour>& atlas);
void buildatlas_area(std::vector<Area>& atlas,
    std::vector<Item>& items, std::vector<Weapon>& weapons,
    std::vector<Armour>& armours, std::vector<Creature>& creatures);

#endif //ATLAS_HPP

atlas.cpp:

#include "atlas.hpp"

void buildatlas_creature(std::vector<Creature>& atlas)
{
    //fill the atlas
    //Creature(name, health, str, end, dex, hitRate, level)
    atlas.push_back(Creature("Rat", 8, 8, 8, 12, 2.0, 1));

    return;
}
void buildatlas_item(std::vector<Item>& atlas)
{
    //Item(name, description)
    atlas.push_back(Item("Gold Coin", 
        "A small disc made of lustrous metal"));
    atlas.push_back(Item("Iron Key",
        "A heavy iron key with a simple cut"));

    return;
}
void buildatlas_weapon(std::vector<Weapon>& atlas)
{
    //Weapon(name, description, damage, hitRate)
    atlas.push_back(Weapon("Iron Dagger", 
        "A short blade made of iron with a leather hilt",
        5, 10.0));
    atlas.push_back(Weapon("Excalibur",
        "The legendary blade, bestowed upon you by the lady of the lake",
        35, 35.0));

    return;
}
void buildatlas_armours(std::vector<Armour>& atlas)
{
    //Armour(name, description, defence, slot)
    atlas.push_back(Armour("Leather Vest",
        "Torso armour made of tanned hide",
        4, Armour::Slot::TORSO));

    return;
}
void buildatlas_area(std::vector<Area>& atlas,
    std::vector<Item>& items, std::vector<Weapon>& weapons,
    std::vector<Armour>& armours, std::vector<Creature>& creatures)
{
    // Area definitions are somewhat more complicated:
    atlas.push_back(Area(Dialogue(        // Standard dialogue definiton
        "You are in room 1",              // Description
        { "Go to room 2", "Search" }),      // Choices
        Inventory(                        // Area inventory
    {
        std::make_pair(&items[0], 5)  // Pair of item and quantity

    },
    {
        std::make_pair(&weapons[0], 1)// Pair of weapon and quantity
    },
    {
        std::make_pair(&armours[0], 1) // Pair of armour and quantity
    }),
    {                                 // Creatures
    }));

    atlas.push_back(Area(Dialogue(
        "You are in room 2",
        { "Go to room 1", "Search" }),
        Inventory(
    {
        std::make_pair(&items[0], 10),
        std::make_pair(&items[1], 1)
    },
    {
    },
    {
    }),
    {
        &creatures[0]
    }));

    return;
}

inventory.hpp:

#pragma once
#ifndef INVENTORY_HPP
#define INVENTORY_HPP

#include "item.hpp"
#include "weapon.hpp"
#include "armour.hpp"

#include <list>
#include <utility>
#include <iostream>

class Inventory
{
public:
    //Whilst weapons and armour are items, they have their own 
    //specific properties and can't be stored in the same list.
    //The first element of the pair stores a pointer to the item
    //in the item/weapon/armour atlas, defined in main(), the second
    //if the quantity of the item

    std::list<std::pair<Item*, int>> items;
    std::list<std::pair<Weapon*, int>> weapons;
    std::list<std::pair<Armour*, int>> armours;

    Inventory(){};
    Inventory(std::list<std::pair<Item*, int>> items,
        std::list<std::pair<Weapon*, int>> weapons,
        std::list<std::pair<Armour*, int>> armours)
    {
        this->items = items;
        this->weapons = weapons;
        this->armours = armours;
    }

    void add_item(Item* item, int count);
    void add_weapon(Weapon* weapon, int count);
    void add_armour(Armour* armour, int count);

    void remove_item(Item*, int count);
    void remove_weapon(Weapon*, int count);
    void remove_armour(Armour*, int count);

    void merge(Inventory* inventory);
    void clear();

    int print_items(bool label);
    int print_weapons(bool label);
    int print_armours(bool label);
    void print(bool label);
};

//add an item to the inventory
void Inventory::add_item(Item* item, int count)
{
    // Increase the quantity if the item already exists
    for (auto& it : this->items)
    {
        if (it.first == item) it.second += count;
        return;
    }
    // If the item doesn't already exist in the inventory, then a
    // pair must be created too
    this->items.push_back(std::make_pair(item, count));
}
void Inventory::add_weapon(Weapon* weapon, int count)
{
    for (auto& it : this->weapons)
    {
        if (it.first == weapon)
        {
            it.second += count;
            return;
        }
    }
    this->weapons.push_back(std::make_pair(weapon, count));
}

void Inventory::add_armour(Armour* armour, int count)
{
    for (auto& it : this->armours)
    {
        if (it.first == armour)
        {
            it.second += count;
            return;
        }
    }
    this->armours.push_back(std::make_pair(armour, count));
}

void Inventory::remove_item(Item* item, int count)
{
    // Iterate through the items, and if they are found then decrease
    // the quantity by the quantity removed
    for (auto& it : this->items)
    {
        if (it.first == item) it.second -= count;
    }
    // Iterate through the list again, and remove any elements from
    // the list that have zero or less for their quantity
    // We do this in two passes because removing an element from
    // a list during a for loop invalidates the iterators, and the
    // loop stops working
    this->items.remove_if([](std::pair<Item*, int>& element)
    {
        return element.second < 1;
    });
}

void Inventory::remove_weapon(Weapon* weapon, int count)
{
    for (auto& it : this->weapons)
    {
        if (it.first == weapon) it.second -= count;
    }
    this->weapons.remove_if([](std::pair<Weapon*, int>& element)
    {
        return element.second < 1;
    });
}

void Inventory::remove_armour(Armour* armour, int count)
{
    for (auto& it : this->armours)
    {
        if (it.first == armour) it.second -= count;
    }
    this->armours.remove_if([](std::pair<Armour*, int>& element)
    {
        return element.second < 1;
    });
}

//merge the specified Inventory with the current one
//adding item quantaties if they exist, creating a new slot if they don't
void Inventory::merge(Inventory* inventory)
{
    // You can't merge an inventory with itself!
    if (inventory == this) return;

    // Loop through the items to be added, and add them. Our addition
    // function will take care of everything else for us
    for (auto it : inventory->items)
    {
        this->add_item(it.first, it.second);
    }
    // Do the same for the weapons
    for (auto it : inventory->weapons)
    {
        this->add_weapon(it.first, it.second);
    }
    // Do the same for the armour
    for (auto it : inventory->armours)
    {
        this->add_armour(it.first, it.second);
    }

    return;
}

void Inventory::clear()
{
    this->items.clear();
    this->weapons.clear();
    this->armours.clear();
}

//output a list of items to stdout, nicely formatted
int Inventory::print_items(bool label = false)
{
    unsigned int i = 1;

    for(std::list<std::pair<Item*, int>>::iterator it =
        this->items.begin(); it != this->items.end(); ++it)
    {
        //number items if asked
        if(label)
        {
            std::cout << i++ << ":";
            //output the item name, quantity and description
            std::cout << it->first->name << " (" << it->second << ") - ";
            std::cout <<it->first->description << std::endl;
        }
    }
    //return number of items outputted, for convienience
    return this->items.size();
}

//output a list of weapons to stdout, nicely formatted
int Inventory::print_weapons(bool label = false)
{
    unsigned int i = 1;

    for (auto it : this->weapons)
    {
        if (label) std::cout << i++ << ": ";
        std::cout << it.first->name << " (" << it.second << ") - ";
        std::cout << it.first->description << std::endl;
    }

    return this->weapons.size();
}

//output a list of armour to stdout, nicely formatted
int Inventory::print_armours(bool label = false)
{
    unsigned int i = 1;

    for(std::list<std::pair<Armour*, int>>::iterator it =
        this->armours.begin(); it != this->armours.end(); ++it)
    {
        //number items if asked
        if(label)
        {
            std::cout << i++ << ":";
            //output the item name, quantity and description
            std::cout << it->first->name << " (" << it->second << ") - ";
            std::cout <<it->first->description << std::endl;
        }
    }
    //return number of items outputted, for convienience
    return this->armours.size();
}

//print the entire inventory, unless it is empty
void Inventory::print(bool label = false)
{
    if(this->items.size() == 0 && 
        this->weapons.size() == 0 &&
        this->armours.size() == 0)
    {
        std::cout << "Empty!" << std::endl;
    }
    else
    {
        this->print_items(label);
        this->print_weapons(label);
        this->print_armours(label);
    }
    return;
}
#endif //INVENTORY_HPP

main.cpp:

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <utility>
#include <cstdlib>
#include <ctime>

#include "area.hpp"
#include "armour.hpp"
#include "atlas.hpp"
#include "creature.hpp"
#include "dialogue.hpp"
#include "inventory.hpp"
#include "item.hpp"
#include "weapon.hpp"

Creature dialogue_newChar();

int main(void)
{
    std::vector<Creature> creatureAtlas;
    std::vector<Item> itemAtlas;
    std::vector<Weapon> weaponAtlas;
    std::vector<Armour> armourAtlas;
    std::vector<Area> areaAtlas;

    // Build the atlases
    buildatlas_creature(creatureAtlas);
    buildatlas_item(itemAtlas);
    buildatlas_weapon(weaponAtlas);
    buildatlas_armours(armourAtlas);
    buildatlas_area(areaAtlas, itemAtlas, weaponAtlas,
        armourAtlas, creatureAtlas);

    Creature player;

    //seed rand() with system time, to produce better random numbers
    srand(time(NULL));

    //main game menu dialogue
    int result = Dialogue("Welcome to the jungle!", { "New Game" }).activate();

    switch (result)
    {
    case 1:
        player = dialogue_newChar();
        break;
    default:
        return 0;
        break;
    }
    // Set the current area to be the first area in the atlas,
    // essentially placing the player there upon game start
    Area* currentArea = &(areaAtlas[0]);

    // Play the game until a function breaks the loop and closes it
    while (1)
    {
        // If the player has died then inform them as such and close
        // the program
        if (player.health <= 0)
        {
            std::cout << "\t----YOU DIED----\n    Game Over\n";
            return 0;
        }

        // Activate the current area's dialogue
        result = currentArea->dialogue.activate();

        // These could be moved inside of the area code using an
        // event style system, but that allows for much less
        // flexibility with what happens in each area. Since we're
        // defining the areas in code anyway, sticking with this
        // isn't too much of a problem, and it keeps things easy
        // to understand
        if (currentArea == &(areaAtlas[0]))
        {
            switch (result)
            {
            case 1:
                // Move to area 1
                currentArea = &(areaAtlas[1]);
                break;
            case 2:
                // Search the area
                currentArea->search(player);
                break;
            default:
                break;
            }
        }
        else if (currentArea == &(areaAtlas[1]))
        {
            switch (result)
            {
                // Move to area 0
            case 1:
                currentArea = &(areaAtlas[0]);
                break;
                // Search the area
            case 2:
                currentArea->search(player);
                break;
            default:
                break;
            }
        }
    }

    return 0;

}

//create a new character
Creature dialogue_newChar()
{
    //ask for a name and class
    std::cout << "Choose your name punk!" << std::endl;
    std::string name;
    std::cin >> name;

    int result = Dialogue("Choose your class!", { "Fighter", "Rogue" }).activate();

    switch (result)
    {
    case 1:
        //Fighter favours health and strength
        return Creature(name, 35, 20, 10, 5, 10.0, 1, "Fighter");
        break;
    case 2:
        //Rogue favours endurance and dexterity
        return Creature(name, 30, 5, 10, 20, 15.0, 1, "Rogue");
        break;
    default:
        //default should not happen, good to be safe though!
        return Creature(name, 30, 10, 10, 10, 10.0, 1, "Adventurer");
        break;
    }
}

如果需要,我可以为其他文件添加代码。 谢谢!

当你定义成员函数时,你有以下选择来防止多重定义:

  1. 将它们放在 .cpp 文件中,而不是放在 .hpp 文件中,就像您现在有一些文件一样。这是最喜欢的。

  2. 将它们内嵌在 class 正文中。在某些情况下适合单行。

  3. 将它们放在 class 主体之外,但用 inline 关键字标记它们。

现在您在头文件中的 class 主体之外定义了成员函数,并且没有用 inline 标记。我建议您执行 #1 并将它们移至 .cpp 文件。

您需要将 Inventory class 方法的实现移动到 .cpp 文件中。

发生的事情是头文件包含在几个不同的 .cpp 文件中,因此这些方法在几个不同的编译单元中编译。这会导致链接错误,表明这些方法已经存在于不同的编译单元中。

事实上,除了模板实现之外的所有实现都应该存在于它们自己的 .cpp 文件中,正是出于这个原因。

当您尝试将具有 class Inventory 方法实现的单个 inventory.hpp 文件包含到不同的 .cppmain.cppatlas.cpp 通过 atlas.hpp) 文件。 如果您将 class Inventory 的接口和实现分开,那么它将解决您的问题。

分离也很重要,因为

  1. 您隐藏了实现,因此客户端代码必须通过其 public 接口使用一个对象
  2. 它减少了项目的构建时间
  3. 您可以更改实现 .cpp 文件而不更改接口文件 .hpp

有关分离的更多信息,请参阅文章 Separating Interface and Implementation in C++

#include "*.hpp" 

将多次定义您的方法。创建一个 *.h 文件,在那里声明所有方法并使用

#include "*.h"

*.cpp 文件中定义您的方法。

我遇到了类似的问题。然后我意识到我在单独的 .cpp 文件中对 .h 文件有多个声明。这解决了问题。