【问题标题】:How to properly create a header for functions, without multiple definitions error?如何正确创建函数的标头,而不会出现多个定义错误?
【发布时间】:2021-04-04 05:19:30
【问题描述】:

我有一个标题,它定义了我需要的一些功能。我将它们包含在两个文件中,这些文件本身包含在 main.cpp 中,但是我得到了函数错误的多个定义,尽管我放了

#ifndef MYFUNCTION_H
#define MYFUNCTION_H
//code
#endif

在我的代码中。 那么我做错了什么?这是我的标题:

#ifndef EXTRASFMLFUNCTIONS_H
#define EXTRASFMLFUNCTIONS_H

#include <SFML/System/Vector2.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Text.hpp>

inline void setCenterPos(sf::Sprite &entity, const sf::Vector2f& position)
{
    entity.setPosition(sf::Vector2f{(position.x - entity.getGlobalBounds().width / 2), position.y - (entity.getGlobalBounds().height / 2)});
}

inline void setCenterPos(sf::Text &entity, const sf::Vector2f& position)
{
    entity.setPosition(sf::Vector2f{(position.x - entity.getGlobalBounds().width / 2), position.y - (entity.getGlobalBounds().height / 2)});
}

inline sf::Vector2f getCenter(const sf::Sprite& entity)
{
    return sf::Vector2f{entity.getGlobalBounds().width / 2, entity.getGlobalBounds().height / 2} + entity.getPosition();
}

inline void scaleTo(sf::Sprite& entity, const sf::Vector2f& size)
{
    entity.scale(size.x / entity.getGlobalBounds().width, size.y / entity.getGlobalBounds().height);
    return;
}

inline void scaleToWidth(sf::Sprite& entity, const float &width)
{
    entity.scale(width / entity.getGlobalBounds().width, width / entity.getGlobalBounds().width);
    return;
}

inline void scaleToHeight(sf::Sprite& entity, const float &height)
{
    entity.scale(height / entity.getGlobalBounds().height, height / entity.getGlobalBounds().height);
    return;
}

#endif

编辑:它与 inline 关键字一起使用

【问题讨论】:

  • 重要提示:标题保护,ifndef 的东西,防止在单个 translation unit 中包含多个内容。一个 cpp 文件不会多次在标头中包含这些内容。两个 cpp 文件可以每个包含一次标头,当链接在一起时,可能会出现多个定义。
  • 该标题中的所有内容都是inline,这应该可以解决任何多个定义问题。你正在做一些奇怪的事情,你的问题没有很好地解释。请使用minimal reproducible example 更新问题。我们可以用来重现问题。
  • 我复制粘贴错了,抱歉,有些函数应该有类型
  • 使用static inline 而不仅仅是inline

标签: c++ function compiler-errors definition


【解决方案1】:

在 C++ 中,单一定义规则 (ODR) 规定对象和非内联函数在整个程序和模板中不能有多个定义,并且类型不能按翻译单元有多个定义。正如cppreference 在单一定义规则下所说:

每个非内联函数或变量的一个且只有一个定义 odr-used(见下文)必须出现在整个 程序(包括任何标准和用户定义的库)。这 编译器不需要诊断这种违规行为,但行为 违反它的程序是未定义的。

这就是为什么在不内联函数时会出现多个定义链接错误的原因。这些函数包含(字面复制)到实现文件中。标头保护不会阻止这种情况,因为文件是单独包含的。因此,您违反了 ODR,因为具有外部链接的相同函数被多次定义。

当您内联函数时,每个翻译单元(实现文件 + 它的所有包含文件)都会获得自己的函数副本。当链接器链接目标文件时,这不会被视为违反 ODR,因为 inline 关键字。这就是inline 的特别之处:它告诉链接器同一个函数的多个定义不是错误。

你有两个选择:

  • 像你做的那样内联函数;
  • 将函数声明放在头文件中,并将定义放在一个 cpp 文件中

我会选择第一个选项,因为功能很短而且不那么复杂。

【讨论】:

    【解决方案2】:

    一年后,我得知 inline 关键字已经失去了原来的意义。它曾经被用来充当预处理指令,类似于将函数复制粘贴到调用它的任何位置,而不是执行所有函数调用的跳转指令。如果你调用你的函数 100 万次,它会快一点,但显然,编译器现在自己做。 inline 现在的意思是'是的,这个东西可以定义多次,没关系'。

    【讨论】:

    • jignatius 正确回答了我觉得的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-20
    • 2021-04-26
    • 2019-10-22
    • 2015-03-31
    • 1970-01-01
    • 1970-01-01
    • 2017-10-09
    相关资源
    最近更新 更多