从文件中引用不同名称的 class' 方法
Refering a differently named class' method from a file
我正在尝试了解 c++/g++ 编译器的加载器及其使用的约定。
我有四个源文件。
Hello.h
Hello.cpp
Hello1.cpp
main.cpp
Hello.h
#include <iostream>
class Hello1
{
public:
int a;
void sayHello();
};
Hello.cpp
#include"Hello.h"
void Hello1::sayHello()
{
std::cout<<this->a;
}
Hello1.cpp
#include"Hello.h"
void Hello1::sayHello()
{
std::cout<<"Hello";
}
main.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
我知道如果我将 class Hello 命名为 Hello 并包含相应的 Hello.cpp 加载程序将找到函数定义并执行成员函数。但是,如果我将头文件 Hello.h 中的 class 的名称从 Hello 更改为 Hello1,则目标文件的创建没有问题,并且编译器知道 class Hello1 存在并分配内存为它(猜测c++ -c命令成功)
但是加载器找不到 sayHello() 的函数体。这似乎没有考虑 Hello.cpp 或 Hello1.cpp,因为 Hello.h 与 class 有不同的 class 你好 那么如何加载程序即使在正常情况下也加载函数定义?它是引用文件名 Hello.h 并查找 Hello.cpp ,还是引用 class 名称 Hello1 并查找 Hello1.cpp ,或者它是否具有约束检查以查看 .h 和 class 名称是否相同,然后只查找同名的 .cpp 并忽略头文件中的其余 classes ?
如果一些 C++ 大师可以给我一些见解,让我了解加载程序在普通 C++ 文件中获取#include 中包含的定义的基础是什么,在这种情况下如何引用通过使用不同的名称本身来定义 sayHello() 是否可能?或者头文件是否可以仅提供与 class 具有相同名称
的接口
您需要告诉链接器要使用哪个文件对象 (.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 文件)的名称不需要与内部定义的 class 的名称相匹配。
所以这是合法的:
foo.h
class Bar
{
public:
void someFunc();
}
简短版本:您提供了一组提供符号列表的文件。 您(或构建系统)负责通过指定正确的文件来提供“正确的”符号列表(及其定义)。这些文件的名称是 Hello、Hello1、foo 还是 bar(+ 适当的后缀)并不重要
让我们通过objdump -t -C main.o
看看c++ -c main.cpp
的结果
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)。
如果链接器的符号列表中有这样的符号(并且适合),则可以解决依赖关系。如果没有这样的符号,您会收到“未定义的引用”/“未解析的符号”错误消息。
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++/g++ 编译器的加载器及其使用的约定。
我有四个源文件。
Hello.h
Hello.cpp
Hello1.cpp
main.cpp
Hello.h
#include <iostream>
class Hello1
{
public:
int a;
void sayHello();
};
Hello.cpp
#include"Hello.h"
void Hello1::sayHello()
{
std::cout<<this->a;
}
Hello1.cpp
#include"Hello.h"
void Hello1::sayHello()
{
std::cout<<"Hello";
}
main.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
我知道如果我将 class Hello 命名为 Hello 并包含相应的 Hello.cpp 加载程序将找到函数定义并执行成员函数。但是,如果我将头文件 Hello.h 中的 class 的名称从 Hello 更改为 Hello1,则目标文件的创建没有问题,并且编译器知道 class Hello1 存在并分配内存为它(猜测c++ -c命令成功)
但是加载器找不到 sayHello() 的函数体。这似乎没有考虑 Hello.cpp 或 Hello1.cpp,因为 Hello.h 与 class 有不同的 class 你好 那么如何加载程序即使在正常情况下也加载函数定义?它是引用文件名 Hello.h 并查找 Hello.cpp ,还是引用 class 名称 Hello1 并查找 Hello1.cpp ,或者它是否具有约束检查以查看 .h 和 class 名称是否相同,然后只查找同名的 .cpp 并忽略头文件中的其余 classes ?
如果一些 C++ 大师可以给我一些见解,让我了解加载程序在普通 C++ 文件中获取#include 中包含的定义的基础是什么,在这种情况下如何引用通过使用不同的名称本身来定义 sayHello() 是否可能?或者头文件是否可以仅提供与 class 具有相同名称
的接口您需要告诉链接器要使用哪个文件对象 (.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 文件)的名称不需要与内部定义的 class 的名称相匹配。
所以这是合法的:
foo.h
class Bar
{
public:
void someFunc();
}
简短版本:您提供了一组提供符号列表的文件。 您(或构建系统)负责通过指定正确的文件来提供“正确的”符号列表(及其定义)。这些文件的名称是 Hello、Hello1、foo 还是 bar(+ 适当的后缀)并不重要
让我们通过objdump -t -C main.o
c++ -c main.cpp
的结果
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)。
如果链接器的符号列表中有这样的符号(并且适合),则可以解决依赖关系。如果没有这样的符号,您会收到“未定义的引用”/“未解析的符号”错误消息。
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()' 错误,因为有两个同名的符号(并且没有解决该问题的机制....)。