【问题标题】:Confused about relation between header and implementation file对头文件和实现文件之间的关系感到困惑
【发布时间】:2012-04-18 19:09:41
【问题描述】:

我正在使用 Code::Blocks。当我创建一个头文件foo.h 并在其中提出声明以及实现时,编译工作正常。创建一个foo.cpp 文件并在其中添加任何内容仍然有效。但是当我在实现文件中包含头文件,但将实现保留在头文件中时,我收到一个关于 multiple declarations 的错误。

我当前项目的示例

// header-file `GKit/math/blendprocs.cpp`
#include "GKit/utils/Color.h"

#ifndef _GKIT_MATH_BLENDPROCS_H_
#define _GKIT_MATH_BLENDPROCS_H_

    namespace GKit {
        namespace math {

            void blendAoverB(GKIT_COLORBLENDPROC_ARGSX) {
                D.a = A.a + (1 - A.a) * B.a;
                D.r = (A.a * A.r + (1 - A.a) * B.a * B.r) / D.a;
                D.g = (A.a * A.g + (1 - A.a) * B.a * B.g) / D.a;
                D.b = (A.a * A.b + (1 - A.a) * B.a * B.b) / D.a;
                GKIT_UTILS_COLOR_NORMALIZE(D);
            }

        }
    }

#endif // _GKIT_MATH_BLENDPROCS_H_

// implementation-file `GKit/math/blendprocs.cpp`
#include "GKit/math/blendprocs.h"

// Compilation output
-------------- Build: Debug in GKit ---------------

Compiling: GKit\math\blendprocs.cpp
Linking console executable: bin_debug\GKit.exe
build_debug\GKit\math\blendprocs.o:C:\Users\niklas\Desktop\GKit/./GKit/math/blendprocs.h:10: multiple definition of `GKit::math::blendAoverB(GKit::utils::Color const&, GKit::utils::Color const&, GKit::utils::Color&)'
build_debug\main.o:C:\Users\niklas\Desktop\GKit/./GKit/math/blendprocs.h:10: first defined here
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
2 errors, 0 warnings

你能帮我理解这个错误吗?

【问题讨论】:

    标签: c++ compilation compiler-errors g++ mingw


    【解决方案1】:

    您在标题中有定义。该定义将在包含该标头的任何源文件中重复;因为在程序中有多个定义通常是错误的,所以构建失败。这被称为单一定义规则,有时也被称为 ODR。

    两个最佳选择是:

    • 将定义移到源文件中,只在标题中留下声明,或者
    • 在定义中添加inline;这放宽了规则并允许在一个程序中进行多个定义,只要它们都是相同的。

    此外,您不应该为包含保护使用保留名称;您应该从_GKIT_MATH_BLENDPROCS_H_ 中删除最初的_;最好将这些守卫放在标题的开头,在任何#include 指令之前。

    【讨论】:

      【解决方案2】:

      在您的头文件中,您通常只会输入:

      // header-file `GKit/math/blendprocs.cpp`
      #ifndef _GKIT_MATH_BLENDPROCS_H_
      #define _GKIT_MATH_BLENDPROCS_H_
      
      #include "GKit/utils/Color.h"
      
      namespace GKit {
          namespace math {
      
              void blendAoverB(GKIT_COLORBLENDPROC_ARGSX);
      
          }
      }
      
      #endif // _GKIT_MATH_BLENDPROCS_H_
      

      然后在您的 CPP 文件中放置实际的实现:

      // implementation-file `GKit/math/blendprocs.cpp`
      #include "GKit/math/blendprocs.h"
      
      namespace GKit {
          namespace math {
      
              void blendAoverB(GKIT_COLORBLENDPROC_ARGSX) {
                  D.a = A.a + (1 - A.a) * B.a;
                  D.r = (A.a * A.r + (1 - A.a) * B.a * B.r) / D.a;
                  D.g = (A.a * A.g + (1 - A.a) * B.a * B.g) / D.a;
                  D.b = (A.a * A.b + (1 - A.a) * B.a * B.b) / D.a;
                  GKIT_UTILS_COLOR_NORMALIZE(D);
              }
      
          }
      }
      

      否则,您的函数的实现将在包含标头的所有 CPP 文件中结束,这会使链接器在尝试解析 blendAoverB 的地址时感到困惑,因为它可以在其对应的所有目标文件中找到它CPP 文件包含实现的头文件。

      附带说明,最好将包含防护(#ifndef _GKIT_MATH_BLENDPROCS_H_)放在头文件中的其他任何内容之前(cmets 除外)。

      【讨论】:

        【解决方案3】:

        您应该内联您的实现或将实现放在 cpp 文件中,而不是两者兼而有之。

        // header "GKit/math/blendprocs.h"
        #pragma once
        
        #include "GKit/utils/Color.h"
        
        namespace GKit {
                namespace math {
                    void blendAoverB(GKIT_COLORBLENDPROC_ARGSX);
                }
            }
        
        // implementation "GKit/math/blendprocs.cpp"
        #include "GKit/math/blendprocs.h"
        
        namespace GKit {
                namespace math {
        
                    void blendAoverB(GKIT_COLORBLENDPROC_ARGSX) {
                        D.a = A.a + (1 - A.a) * B.a;
                        D.r = (A.a * A.r + (1 - A.a) * B.a * B.r) / D.a;
                        D.g = (A.a * A.g + (1 - A.a) * B.a * B.g) / D.a;
                        D.b = (A.a * A.b + (1 - A.a) * B.a * B.b) / D.a;
                        GKIT_UTILS_COLOR_NORMALIZE(D);
                    }
        
                }
            }
        

        或者使用 inline 关键字将其正确内联到标题中。

        // header "GKit/math/blendprocs.h"
        #pragma once
        
        #include "GKit/utils/Color.h"
        
            namespace GKit {
                namespace math {
                    inline void blendAoverB(GKIT_COLORBLENDPROC_ARGSX) {
                        D.a = A.a + (1 - A.a) * B.a;
                        D.r = (A.a * A.r + (1 - A.a) * B.a * B.r) / D.a;
                        D.g = (A.a * A.g + (1 - A.a) * B.a * B.g) / D.a;
                        D.b = (A.a * A.b + (1 - A.a) * B.a * B.b) / D.a;
                        GKIT_UTILS_COLOR_NORMALIZE(D);
                    }
                }
            }
        

        【讨论】:

          【解决方案4】:

          这正是消息所说的。您应该熟悉内联和 ODR。你不能有多个定义,它是针对 ODR 的。我猜你的 blendprocs.cpp 不止一次包含在不同的编译单元中。与Why aren't my compile guards preventing multiple definition inclusions?相关

          ODR:

          http://en.wikipedia.org/wiki/One_Definition_Rule

          【讨论】:

            猜你喜欢
            • 2023-03-09
            • 1970-01-01
            • 1970-01-01
            • 2012-07-16
            • 2018-03-05
            • 2015-11-29
            • 1970-01-01
            • 1970-01-01
            • 2021-11-28
            相关资源
            最近更新 更多