gpt4 book ai didi

c++ - 从文件中引用不同名称的类的方法

转载 作者:行者123 更新时间:2023-11-30 01:11:04 37 4
gpt4 key购买 nike

我正在尝试了解 c++/g++ 编译器的加载器及其使用的约定。

我有四个源文件。

你好.h
你好.cpp
你好1.cpp
main.cpp

你好.h

#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 。但是当链接和加载生成可执行文件时 ie c++ main.o 它给出了一个错误,说找不到函数定义
main.o: In function main':
main.cpp:(.text+0x19): undefined reference to
Hello1::sayHello()'collect2: ld returned 1 exit status
我知道如果我将类命名为 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 的定义() 通过使用不同的名称本身,有可能吗?还是头文件只能为具有相同名称的类提供接口(interface)

最佳答案

简短版本:提供一组提供符号列表的文件。 (或构建系统)负责通过指定正确的文件来提供“正确的”符号列表(及其定义)。这些文件的名称是 Hello、Hello1、foo 还是 bar(+ 适当的后缀)并不重要


让我们看看c++ -c main.cpp via objdump -t -C main.o 的结果

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
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 g F .text 00000050 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

有一个符号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)。
如果链接器的符号列表中有这样的符号(并且适合),则可以解决依赖关系。如果没有这样的符号,您会收到“ undefined reference ”/“未解析的符号”错误消息。
IE。您必须提供一个定义所需符号的对象(文件),否则链接器将失败。该文件的名称无关紧要。

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,如果你调用 ld [...] main.o Hello1.o Hello1.o 的 Hello1::sayHallo() 被使用。
现在调用 c++ main.cpp Hello.cpp Hello1.cpp 你会得到一个“Hello.cpp:(.text+0x0): re-definition of `Hello1::sayHello()' 错误因为有两个符号具有相同的名称(并且没有解决该问题的机制....)。

关于c++ - 从文件中引用不同名称的类的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36475959/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com