Linker c++;转发声明、翻译单元、链接

Linker c++;转发声明、翻译单元、链接,linker,external,declaration,forward,Linker,External,Declaration,Forward,我读到: 简单地说“有这样一个类”,它的完整定义将是“以后再来”(在当前文件中,在编译时,或者在链接时从其他文件中) 我不确定我在链接时是否理解这个过程。我写了下面的代码来演示它。如果我错了,请纠正我。我不确定链接时的转发声明是如何工作的 //first.h ----------- class Second; class First{ public: Second* ptr; First(); }; //first.cpp ----------- #include "firs

我读到:

简单地说“有这样一个类”,它的完整定义将是“以后再来”(在当前文件中,在编译时,或者在链接时从其他文件中)

我不确定我在链接时是否理解这个过程。我写了下面的代码来演示它。如果我错了,请纠正我。我不确定链接时的转发声明是如何工作的

//first.h
-----------
class Second;

class First{
public:
    Second* ptr;
    First();
};

//first.cpp
-----------
#include "first.h"
extern Second second;
First::First(){ptr = &second;}

//second.h
----------
class Second{
public:
    Second(){};
};

//main.cpp
----------
#include "second.h"
Second second;

int main(int argc, char *argv[])
{
    return 0;
}
这段代码是编译的。如果第二行;被注释时,链接器抛出:对“second”的未定义引用。
把一些评论放在一起1)转发声明2)编译单元3)链接可能会有所帮助。

我认为您所阅读的文档过于松散,误导了您:

class MyClass;
这并不意味着有这样一个类,因为 使类存在的唯一方法是定义它,并且声明是必需的 这不是一个定义。该声明最好理解为:假设存在这样一个类

这并不意味着这个类的完整定义将在以后出现,也不会出现。它是 完整的定义可能需要稍后才能成功编译。或 不如果以后需要完整的类定义, 它需要来进行成功的汇编;因此,在编译时,而不是链接时

您能够引发的未定义引用链接错误 通过注释掉
Secondmain.cpp
中的code>只是一个 普通的旧的未定义的引用错误,如您经常遇到的错误 正在尝试链接一个程序,其中一个变量声明为
extern
在某个地方被引用,而在任何地方都没有定义。它没有必要 与类类型的
extern
变量的连接 比,比如说,
int
-或者与转发类声明业务相关

类的前向声明只有在抢占时才有必要 当编译器试图解析的定义时出现死锁 两个相互依赖且无法完成的类 在完成另一个类定义之前,先定义一个类

一个简单的例子:我天真地编写了两个类
第一个
第二个
,其中 每个类都有一个方法,该方法使用另一个类的对象并调用 其方法之一是:

first.h

#ifndef FIRST_H
#define FIRST_H

#include <string>
#include <iostream>
#include "second.h"

struct first {

    std::string get_type() const {
        return "First";
    }
    void use_a_second(second const & second) const {
        std::cout << second.get_type() << std::endl;
    }

};

#endif
#ifndef SECOND_H
#define SECOND_H

#include <string>
#include <iostream>
#include "first.h"

struct second {

    std::string get_type() const {
        return "First";
    }
    void use_a_first(first const & first) const {
        std::cout << first.get_type() << std::endl;
    }
};

#endif
尝试编译
main.cpp

$ g++ -c -o main.o -Wall -Wextra -pedantic main.cpp 
In file included from first.h:6:0,
                 from main.cpp:1:
second.h:13:19: error: ‘first’ has not been declared
  void use_a_first(first const & first) const {
                   ^~~~~
second.h: In member function ‘void second::use_a_first(const int&) const’:
second.h:14:22: error: request for member ‘get_type’ in ‘first’, which is of non-class type ‘const int’
   std::cout << first.get_type() << std::endl;
                      ^~~~~~~~
main.cpp: In function ‘int main()’:
main.cpp:9:8: error: expected unqualified-id before ‘.’ token
  second.use_a_first(first);
second.h(固定)

链接:

运行:

这是唯一需要转发类声明的场景 需要。它可以在更广泛的环境中使用:请参阅。需要只是每个人的需要 为了成功编译,而不是链接。在之前无法尝试链接 编译成功

文档片段在使用单词定义时也有误导性的不精确。这个 类的定义在编译上下文中意味着一件事,那就是 为了清晰起见,它应该意味着什么。不严格地说,它意味着其他东西, 在联系的背景下,为了清晰起见,这并不意味着这一点。 在链接的上下文中,我们最好只讨论 一个班级——甚至是一个要求资格的概念

就编译器而言,如果类从 从开始到结束:

class foo ... {
    ...
};
没有错误,那么类定义就是该范围的内容。完整的定义 当然,这并不意味着类具有完整的实现。信息技术 只有当,除了一个完整的定义,所有的方法和 在其定义中声明的静态成员本身也在某个地方定义 类定义内的内联;在包含的翻译中不一致 单位,或其他翻译单位(可能在外部编译) 库),编译后的包含翻译单元与之链接。 如果这些成员定义中的任何一个未以其中一种方式提供 到链接时间,将导致未解决的引用链接错误。那个 是类实现的缺陷

<> P>链接器的定义思想不同于C++ 编译器的功能更加简单。从链接器的角度来看, C++类实际上并不存在。对于链接器,类实现由编译器简化, 到一堆符号和符号定义,这些符号和定义与它得到的没有本质上的区别 从任何语言编译器,无论该语言是否处理类。 对于链接器来说,成功的关键在于输出二进制文件中引用的所有符号 要求使用相同的二进制文件或动态库中的定义 在联动装置中。符号(广义上)可以标识一些可执行代码或一些数据。 对于代码符号,定义意味着链接器的实现:定义是表示的代码(如果有)。 对于数据符号,定义意味着链接器的值:它意味着表示的数据(如果有的话)

所以当片段说:

。。它的完整定义将是“稍后”(在当前文件、编译时或链接时来自其他文件)

这需要加以区分

foo
的完整定义必须稍后在 在类型
foo
作为任何其他类型之前,需要一个翻译单元, 特别是基类、函数/方法参数或object1的类型。 如果不满足此要求,将导致编译错误:-

  • 如果未完全定义任何基类,则无法完全定义类
  • 如果函数或方法具有类型的参数,则无法完全定义该函数或方法 这还没有完全定义
  • 对象不能存在任何未完全定义的类型
如果以后不再要求
foo
作为基类、参数或对象的类型, 那么类
foo
的定义永远不需要遵循声明

foo
的完整实现可能是,也可能不是
$ g++ -c -o main.o -Wall -Wextra -pedantic main.cpp 
In file included from first.h:6:0,
                 from main.cpp:1:
second.h:13:19: error: ‘first’ has not been declared
  void use_a_first(first const & first) const {
                   ^~~~~
second.h: In member function ‘void second::use_a_first(const int&) const’:
second.h:14:22: error: request for member ‘get_type’ in ‘first’, which is of non-class type ‘const int’
   std::cout << first.get_type() << std::endl;
                      ^~~~~~~~
main.cpp: In function ‘int main()’:
main.cpp:9:8: error: expected unqualified-id before ‘.’ token
  second.use_a_first(first);
#ifndef FIRST_H
#define FIRST_H

#include <string>

struct second; // Declaration

struct first{
    std::string get_type() const {
        return "first";
    }
    void use_a_second(second const & second) const;
};

#endif  
#ifndef SECOND_H
#define SECOND_H

#include <string>

struct first; //Declaration

struct second{
    std::string get_type() const {
        return "second";
    }
    void use_a_first(first const & first) const;

};

#endif
#include <iostream>
#include "first.h"
#include "second.h"

void first::use_a_second(second const & second) const {
    std::cout << second.get_type() << std::endl;
}
#include <iostream>
#include "first.h"
#include "second.h"

void second::use_a_first(first const & first) const {
    std::cout << first.get_type() << std::endl;
}
$ g++ -c -o first.o -Wall -Wextra -pedantic first.cpp
$ g++ -c -o second.o -Wall -Wextra -pedantic second.cpp
$ g++ -c -o main.o -Wall -Wextra -pedantic main.cpp
$ g++ -o prog main.o first.o second.o
$ ./prog
second
first
class foo ... {
    ...
};
struct first{
    std::string get_type() const {
        return "first";
    }
    void use_a_second(second const & second) const;
    void unused();
};
#include "first.h"
#include "second.h"

int main()
{
    first f;
    second s;
    f.use_a_second(s);
    s.use_a_first(f);
    f.unused();
    return 0;
}
class foo;
foo & bar();
...
foo * pfoo;
...
foo & rfoo = bar();
class foo;
...
foo f; // Error
...
foo * pfoo; 
...
pfoo->method(); // Error