从文件中引用不同名称的类的方法
Refering a differently named class' method from a file
我正在尝试了解c ++/g ++编译器的加载器及其使用的约定。
我有四个源文件.
你好。
你好.cpp
你好1.cpp
主.cpp
你好。
#include <iostream>
class Hello1
{
public:
int a;
void sayHello();
};
你好.cpp
#include"Hello.h"
void Hello1::sayHello()
{
std::cout<<this->a;
}
你好1.cpp
#include"Hello.h"
void Hello1::sayHello()
{
std::cout<<"Hello";
}
主.cpp
#include"Hello.h"
int main()
{
Hello1 hello;
hello.a=5;
hello.sayHello();
return 0;
}
单独预处理和组装每个文件的传递,以及
c++ -c main.cpp也生成一个 main.o .但是当链接和加载以生成可执行文件即c ++ main.o时,它会给出一个错误,指出找不到函数定义 main.o:
在函数main':
main.cpp:(.text+0x19): undefined reference to
Hello1::sayHello(('收集2:LD 返回 1 个退出状态
我知道如果我将类命名为 Hello 并包含相应的 Hello.cpp加载器将找到函数定义并执行成员函数。但是,如果我将头文件 Hello.h 中的类名称从 Hello 更改为 Hello1,则创建目标文件没有问题,并且编译器知道类 Hello1 存在并为其分配内存(猜测 c++ -c 命令的成功(但是加载器找不到 sayHello(( 的函数体。这似乎不是在研究 Hello.cpp 或 Hello1.cpp因为 Hello.h 除了类之外还有一个不同的类Hello 那么即使在正常情况下,加载器如何加载函数定义呢? 它是引用文件名 Hello.h 并查找 Hello.cpp ,还是引用类名 Hello1 并查找 Hello1.cpp , 或者它是否有约束检查以查看 .h 和类名是否相同,然后只查找同名.cpp而忽略头文件中的其余类?
如果一些 c++ 大师能给我一些关于加载器在普通 c++ 文件中拾取 #include 中包含的定义的基础,那就太好了,同样在这种情况下如何通过使用不同的名称本身来引用 sayHello(( 的定义,可能吗? 或者头文件只能包含具有相同名称的类的接口
简短版本:提供一组提供符号列表的文件。您(或构建系统(负责通过指定正确的文件来提供符号的"正确"列表(及其定义(。这些文件是否称为Hello,Hello1,foo或bar(+适当的后缀(并不重要
<小时 />让我们来看看通过objdump -t -C main.o
c++ -c main.cpp
的结果
符号表:
00000000 l df *ABS* 00000000 主.cpp
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l O .bss 00000001 std::__ioinit
00000050 l F .text 00000042 __static_initialization_and_destruction_0(int, int(
00000092 l F .text 0000001a _GLOBAL__sub_I_main
00000000 l d .init_array 00000000 .init_array
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000000 克 F .文本 00000050 主00000000 *UND* 00000000 Hello1::sayHello((
00000000 *UND* 00000000 __stack_chk_fail
00000000 *UND* 00000000 std::ios_base::Init::Init((
00000000 *UND* 00000000 .hidden __dso_handle
00000000 *UND* 00000000 std::ios_base:::Init::~Init((
00000000 *UND* 00000000 __cxa_atexit
有一个符号main
,它是一个函数,它"需要"一些在这个编译单元中没有找到的其他符号.
为了说明这一点,让我们稍微修改一下 main.cpp
#include"Hello.h"
#include <iostream>
// noinline, so that the compiler "keeps" this a function + function calls
void __attribute__ ((noinline)) foo()
{
std::cout << "ho ho ho" << std::endl;
}
int main()
{
Hello1 hello;
hello.a=5;
foo();
hello.sayHello();
return 0;
}
现在输出的objdump...是
SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.cpp
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l O .bss 00000001 std::__ioinit
00000000 l d .rodata 00000000 .rodata
00000084 l F .text 00000042 __static_initialization_and_destruction_0(int, int)
000000c6 l F .text 0000001a _GLOBAL__sub_I__Z3foov
00000000 l d .init_array 00000000 .init_array
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000000 g F .text 0000002f foo()
00000000 *UND* 00000000 std::cout
00000000 *UND* 00000000 std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
00000000 *UND* 00000000 std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
00000000 *UND* 00000000 std::ostream::operator<<(std::ostream& (*)(std::ostream&))
0000002f g F .text 00000055 main
00000000 *UND* 00000000 Hello1::sayHello()
00000000 *UND* 00000000 __stack_chk_fail
00000000 *UND* 00000000 std::ios_base::Init::Init()
00000000 *UND* 00000000 .hidden __dso_handle
00000000 *UND* 00000000 std::ios_base::Init::~Init()
00000000 *UND* 00000000 __cxa_atexit
如您所见,没有*UND* foo()
,编译器可以自行解析该符号+调用.
好的,现在链接器有什么作用?它获取输入文件的列表,并列出这些文件中定义的所有符号。然后,它会查找依赖项并尝试解决它们。 main
"需要"一个符号Hello1::sayHello()
(-C 选项使它看起来像这样,请参阅 https://en.wikipedia.org/wiki/Name_mangling(.
如果链接器的符号列表中有这样的符号(并且适合(,则可以解析依赖关系。如果没有这样的符号,则会收到"未定义的引用"/"未解析的符号"错误消息.
即您必须提供一个定义所需符号的对象(文件(,否则链接器将失败。此文件的名称无关紧要。
Hello.o 提供了一个符号Hello1::sayHello()
,它将满足 main.oc 中引用的要求
...
00000000 g F .text 0000001f Hello1::sayHello()
00000000 *UND* 00000000 std::cout
00000000 *UND* 00000000 std::ostream::operator<<(int)
00000000 *UND* 00000000 std::ios_base::Init::Init()
00000000 *UND* 00000000 .hidden __dso_handle
00000000 *UND* 00000000 std::ios_base::Init::~Init()
00000000 *UND* 00000000 __cxa_atexit
..
Hello1.o 也是如此
...
00000000 g F .text 0000001e Hello1::sayHello()
00000000 *UND* 00000000 std::cout
00000000 *UND* 00000000 std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
00000000 *UND* 00000000 std::ios_base::Init::Init()
00000000 *UND* 00000000 .hidden __dso_handle
00000000 *UND* 00000000 std::ios_base::Init::~Init()
00000000 *UND* 00000000 __cxa_atexit
...
因此,如果你调用(或让 c++/gcc 进行调用(ld [...] main.o Hello.o
符号 Hello1::sayHallo(( 的定义取自 Hello.o,如果你调用 Hello1.o 的 Hello1::sayHallo(( ld [...] main.o Hello1.o
被使用.
现在调用c++ main.cpp Hello.cpp Hello1.cpp
,你会得到一个"Hello.cpp:(.text+0x0(:重新定义'Hello1::sayHello(('错误,因为有两个符号具有相同的名称(并且没有机制如何解决这个问题......
您需要告诉链接器要使用哪个文件对象 (.o( 文件。 Hello.o
或Hello1.o
.所以你的命令行是这样的:
c++ main.o Hello.o
或
c++ main.o Hello1.o
如果您尝试同时使用两者,则会收到如下错误:
$ c++ main.o Hello1.o Hello.o
Hello.o: In function `Hello1::sayHello()':
Hello.cpp:(.text+0x0): multiple definition of `Hello1::sayHello()'
Hello1.o:Hello1.cpp:(.text+0x0): first defined here
collect2: ld returned 1 exit status
在回答您的最后一个问题时,不,头文件(.h 和 .cpp 文件(的名称不需要与内部定义的类的名称匹配。
所以这是合法的:
福.H
class Bar
{
public:
void someFunc();
}
- 如何使用curlpp通过POST方法上传文件和json数据
- 一种在C++中读取TXT配置文件的简单方法
- 在 cpp 文件中隐藏采用模板参数引用的方法
- 在C++中包含原型文件的正确方法是什么?
- 通过比较C++中的行在 txt 文件中搜索的最简单方法是什么?
- 不带预处理器的调用方法/文件的文件名/行号
- 引用文件的适当方法是什么?
- 在文件中写入而不是在 c++ 中使用 "<<" 的替代方法?
- 查找定义我的 C/C++ 函数/宏的文件比'grep'更简单的方法
- 读取大文件(>2GB)(文本文件包含以太网数据)并通过不同参数随机访问数据的最佳方法是什么?
- 在C++中创建文件夹选取器对话框的最简单方法是什么?
- C++:std::ofstream 方法 open() 在第二次迭代时擦除打开的 ifstream 文件
- 如何使用 C/C++ 和 system() 系统调用以外的其他方法在 Linux 中获取文件功能?
- 创建进行生产构建并创建调试信息的C++生成文件的最佳方法?
- 是否有正确的方法对生成文件中的对象文件使用模板命令?(C++)
- 将位字符串转储到二进制文件的最佳方法是什么
- 在目录中查找所有.cpp.h文件(include,src等)的传统方法
- 通过boost asio iostream下载大文件的最快方法是什么?
- LNK2019 在文件中调用静态成员的方法时出错.cpp
- 在 *.cpp 文件中实现的 c++ 函数/方法永远不会内联扩展吗?