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