![](/img/trans.png)
[英]C++: Including header-file fails compilation but including source cpp file compiles
[英]Not including the corresponding header (.h) file in the implementation (.cpp) file still compiles?
我今天寫了一個簡單的示例,只是為了看看它是否可以編譯,當我發現它可以編譯時,我實際上感到非常驚訝!
這是示例:
hello.h
#ifndef HELLO_H
#define HELLO_H
// Function prototype
void say_hello();
#endif
HELLO.CPP
注意:這不包括“ hello.h”,就像我在永遠的歷史中見過的每個C ++示例一樣!
// #include "hello.h" <-- Commented out. The corresponding header is NOT included.
#include <iostream>
void say_hello() {
std::cout << "Hello!" << std::endl;
}
main.cpp中
#include "hello.h"
int main() {
say_hello();
}
然后,我將“ hello.cpp”編譯為靜態庫,如下所示:
g++ -c hello.cpp
ar -rvs libhello.a hello.o
然后,我編譯了“主”應用程序並將其鏈接到庫
g++ -o main main.cpp -L. -lhello
並運行它,它執行得很好!
./main
你好!
當我感到驚訝的時候...我確實理解為什么這樣做。 這是因為“ hello.cpp”中的函數未聲明為靜態,因此具有外部鏈接並且可以從外部看到。 將其設置為靜態將由於未定義引用而導致鏈接失敗。
因此,這就是問題……如果這行得通,那么為什么每個地方的人總是在“ .cpp”實現文件中包括“ .h”頭文件和函數聲明。 顯然,如果僅定義自由函數,則沒有必要,並且如果不包含頭文件,一切都可以正常工作。
那么為什么我們總是將其包括在內? -是否只是對鏈接器的工作原理普遍缺乏了解? 還是還有更多?
讓我們更改您的hello.cpp
:
// #include "hello.h" <-- Commented out. The corresponding header is NOT included.
#include <iostream>
int say_hello() {
std::cout << "Hello!" << std::endl;
return 0;
}
這將與以前的版本一樣編譯。 它也可能會鏈接-但不正確。 返回類型錯誤。
這是未定義的行為,但是在許多常見的實現中,您將不使用它,因為您不使用返回值,並且通常在寄存器中返回它。 但是,不必一定如此-您在運行時可能會遇到非常奇怪的錯誤。 特別是如果差異稍微復雜一些(例如,當調用方期望int
時返回double
-通常會在其他寄存器中返回)。
另一方面,如果您寫了:
#include "hello.h"
#include <iostream>
int say_hello() {
std::cout << "Hello!" << std::endl;
return 0;
}
然后,頭文件中的聲明將與CPP文件中的定義不匹配-並且您將獲得一個不錯的,易於理解的編譯器錯誤消息。
實際上,這是一個好主意,如果您沒有外部函數的聲明,GCC會抱怨。 (如果您在命令行上使用-wall -werror,它將停止您的構建。)
如果您有一個類,則需要包含它以獲取該類及其成員的聲明,以使其定義匹配。 否則,您將無法分開定義和聲明。
/// C.h
class C
{
public:
C();
private:
int _i;
};
/// C.cpp
// #include "C.h"
C::C() : _i(42) {} // error: 'C' does not name a type
看到它在Coliru上失敗了。
同樣,如果您具有類模板或函數模板,則通常需要將其放在標頭中,以便以后可以將其版本標記出來。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.