簡體   English   中英

在實現(.cpp)文件中不包括相應的頭文件(.h)仍可以編譯嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM