【问题标题】:Why do C++ member functions defined in a class not produce duplicate symbols, whereas they do in C?为什么在类中定义的 C++ 成员函数不会产生重复的符号,而在 C 中却会产生重复的符号?
【发布时间】:2014-07-09 03:35:49
【问题描述】:

C 示例

bb.c:

#include "bb.h"
#include <stdio.h>

void bb() {
    printf("aa()...\n");
    aa();
}

main.c:

#include "aa.h"
#include "bb.h"

int main(int argc, const char** argv) {

    aa();
    bb();

    return 0;
}

aa.h:

#ifndef aa_h
#define aa_h

#include <stdio.h>

void aa() {
    printf("aa()...\n");
}

#endif // aa_h

bb.h:

#ifndef bb_h
#define bb_h

#include "aa.h"

void bb();

#endif // bb_h

C 结果

用clang main.c bb.c编译:

duplicate symbol _aa in:
    /var/folders/f2/2w4c0_n519g8cd2k6xv66hc80000gn/T/main-OsFJVB.o
    /var/folders/f2/2w4c0_n519g8cd2k6xv66hc80000gn/T/bb-OkcMzn.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

C++ 示例

b.cpp:

#include "b.hpp"

void b::do_something_else() {
    std::cout << "b::do_something_else() being called..." << std::endl;
    a a;
    a.doit();
}

main.cpp:

#include "a.hpp"
#include "b.hpp"

int main() {

    a a;
    b b;

    a.doit();
    b.do_something_else();

    return 0;
}

a.hpp:

#ifndef a_hpp
#define a_hpp

#include <iostream>

class a{
public:

    void doit() {
        std::cout << "a::doit() being called..." << std::endl;
    }

};

#endif // a_hpp

b.hpp:

#ifndef b_hpp
#define b_hpp

#include "a.hpp"

#include <iostream>

class b{
public:

    void do_something_else();

};

#endif // b_hpp

C++ 结果

上面用clang++ main.cpp b.cpp编译得很好,程序的输出是:

a::doit() being called...
b::do_something_else() being called...
a::doit() being called...

问题

  1. 为什么C++版本会出现重复错误not

  2. 函数void a::doit()定义在头文件而不是源文件中是否意味着编译器会自动内联该函数?

【问题讨论】:

  • 您的 C 示例不是 C。在 C++ 中,a::doit 隐含为 inline
  • 你能解释一下为什么 C 的例子不是 C 吗?
  • 对于初学者来说,C 没有 class 关键字。
  • 你是对的,抱歉我在输出中包含了错误的文件。固定。
  • 你没有理解詹姆斯的问题。我不关心void a::doit()void b::doit() 之间的重复,但事实上void a::doit()a.hppincluded 中都定义 来自@987654340 @ 和 b.hpp。我认为@chris 给出了(有点简短但正确的)答案,因为根据问题 2,a::doit 隐含为inline

标签: c++ c inline-functions duplicate-symbol


【解决方案1】:

在 C++ 中,类方法不是顶级符号,而是类层次结构中的有效范围名称。

这意味着你在 C++ 中定义了两个 doit() 方法,a::doit()b::doit()

在 C 中,您曾尝试定义一个 aa() 函数两次。

请注意,如果您在同一类的范围内定义了两次doit() 方法,C++ 也会出错。

#include <iostream>

class a {

  public:

  void doit() {
    std::cout << "hello" << std::endl;
  }

  void doit() {
    std::cout << "goodbye" << std::endl;
  }
};

导致

ed.cpp:11:8: error: ‘void a::doit()’ cannot be overloaded
   void doit() {
        ^
ed.cpp:7:8: error: with ‘void a::doit()’
   void doit() {
        ^

【讨论】:

  • c++ 内联基本上是优化提示。最好将它们视为“偶尔获得荣誉”。
  • inline 有一个更重要的特性,w.r.t 是单一定义规则;无论它们是否实际上是内联的,如果函数声明为inline,那么只要所有定义都相同,就可以有多个定义。
  • 例如头文件包含void func() { } - 包括来自两个不同单元的两次标题会导致UB,但如果它是inline void func() { }则不会。
  • @MattMcNabb 我同意,谢谢你的解释;但是,如果您想简化这样的解释,则基本上是“不要指望内联实际上内联而不检查它确实这样做了”(用我选择的说法)可以表述为“偶尔荣幸”
  • @JamesH 对,因为正如我写的那样,它不是类层次结构范围内的一个名称,而是试图将两个相同的名称放入该类层次结构范围内。
【解决方案2】:

在您的 C 示例中,aa 被定义了两次,这违反了“一次定义规则”。如果是C++,这同样适用。

在您的C++ 示例中,a::doit 被定义了两次,但它被隐式声明为inline。根据[dcl.fct.spec]/3,在类中定义的成员函数是隐式内联的:

在类定义中定义的函数是内联函数。 ...

inline 函数是一个定义规则的例外(事实上,这是标准要求的 inline 的唯一含义)每个 [basic.def.odr]/5

如果每个定义出现在不同的翻译单元中,并且定义满足以下条件,则程序中可以有多个定义 ... 具有外部链接 (7.1.2) ... 的内联函数要求。 ...

这些要求基本上归结为一个要求,即定义在它们出现的每个翻译单元中都是相同的。

如果您将aa 声明为inline,则会应用类似的规则,并且您的代码将按预期编译和工作。

【讨论】:

    【解决方案3】:

    为什么C++版本不会出现重复错误?

    因为没有重复。 C++ 成员函数的作用域是由定义它们的类确定的。b::doit() 不是 a::doit() 的副本。

    函数 void a::doit() 定义在头文件而不是源文件中是否意味着编译器会自动内联该函数?

    不,但这意味着有可能。

    【讨论】:

      猜你喜欢
      • 2016-02-14
      • 1970-01-01
      • 2016-07-24
      • 1970-01-01
      • 2018-11-26
      • 2015-09-11
      • 2018-03-02
      • 2013-02-06
      • 1970-01-01
      相关资源
      最近更新 更多