【问题标题】:__thiscall already defined in .obj__thiscall 已在 .obj 中定义
【发布时间】:2015-07-27 11:20:59
【问题描述】:

我遇到了链接器错误,我似乎无法找到根本原因,我检查了是否包含 .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;
    }
}

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

【问题讨论】:

    标签: c++ c++11 visual-c++ stl linker


    【解决方案1】:

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

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

    2. 将它们内联到类主体中。在某些情况下适用于单行。

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 啊,有道理!我已将清单类方法移至 .cpp 文件,并且链接器错误已消失。现在修复程序的其余部分!谢谢。
      【解决方案3】:

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

      分离也很重要,因为

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

      更多关于分离的信息你可以阅读文章Separating Interface and Implementation in C++

      【讨论】:

        【解决方案4】:
        #include "*.hpp" 
        

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

        #include "*.h"
        

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

          • “.h 文件的多个声明”是什么意思?意识到某事如何可能解决问题?
          猜你喜欢
          • 2014-11-02
          • 1970-01-01
          • 1970-01-01
          • 2013-02-18
          • 2015-08-10
          • 1970-01-01
          • 2016-11-21
          • 2016-03-09
          • 1970-01-01
          相关资源
          最近更新 更多