在C ++中使用头文件的正确方法是什么?

时间:2016-03-03 03:35:07

标签: c++

我对这个问题有一些疑问,但首先让我给你一些代码:

example.hpp

#ifndef EXAMPLE_H_
#define EXAMPLE_H_

using namespace std;

void say_something(string something);

#endif

example.cpp

#include <string>
#include <iostream>
#include "example.hpp"

void say_something(string something)
{
    cout << something << '\n';
}

的main.cpp

#include <iostream>
#include <string>
#include "example.hpp"

using namespace std;

int main()
{
    string hello = "Hello world!";
    say_something(hello);

    return 0;
}

现在,我的问题是:

  • 我应该在标题 example.hpp 中放置 example.cpp 所需的所有头文件,还是应该将它们保存在示例中。 cpp 就像上面的例子一样?
  • C ++在这种情况下如何工作:我在 main.cpp example.cpp 中包含了'string'和'iostream'...是C ++编译器会用字符串和iostream头文件连接两次(或者你会发现更合格的任何术语)程序?我应该只将字符串和iostream标题放在 example.cpp
  • 最后,我是否应该在标题本身( example.hpp )或实现中添加我将用于示例标题的命名空间( example.cpp )?

3 个答案:

答案 0 :(得分:3)

  

我应该在标题example.hpp中放置example.cpp所需的所有头文件,还是应该像上面的示例一样将它们保存在example.cpp中?

将头文件保持最小。

头文件提供函数声明。函数声明取决于<string>中的内容,而不取决于<iostream>中的内容。因此,标头文件应包含<string>而不包含<iostream>

如果头文件包含不必要的内容,则头文件的用户也将包含那些不必要的内容。

  

C ++在这种情况下如何工作:我在main.cpp和example.cpp中都包含了'string'和'iostream'... C ++编译器是否会链接(或者你会发现更多的术语)使用字符串和iostream标题两次该程序?我应该只在example.cpp

中放置字符串和iostream标头

C ++源代码不与标题链接。每当您编写#include <string>时,预处理器(概念上)都会复制名为“string”的整个头文件并粘贴到#include <string>行。预编译在编译和链接之前进行。

  

最后,我应该在头文件本身(example.hpp)或实现(example.cpp)中使用我将用于示例头的命名空间吗?

永远不要在头文件的全局范围内写using namespace std;。由于#include的复制和粘贴特性,using-directive将感染包含标头的其他文件。如果有文件想要使用say_something但又不想using namespace std该怎么办?

答案 1 :(得分:1)

  

我是否应该将example.cpp所需的所有头文件放入其中   标题example.hpp或者我应该将它们保存在example.cpp中   上面的例子?

不,你不应该。头文件应该只包含它需要的头文件。

  

C ++在这种情况下如何工作:我在main.cpp和example.cpp中都包含了'string'和'iostream'... C ++编译器是否会链接(或者你会发现更多的术语)使用字符串和iostream标题两次该程序?我应该只在example.cpp

中放置字符串和iostream标头

由于包含警卫,编译器不会将stringiostream链接两次。它只会打开 string header 例如,一旦包含守卫告诉编译器它已经被包含就返回。

  

最后,我应该在头文件本身(example.hpp)或实现(example.cpp)中使用我将用于示例头的命名空间吗?

这与包含问题相同。如果在头文件中放置“using namespace std;”,则包含它的每个其他文件将被强制使用整个命名空间。顺便说一下,这不是一件好事。

因此,在您的实施中use namespace std并不邪恶(包含所有标题后)。

在你的头文件中,在函数体内使用“using namespace std”或“using std::string”也没关系,它们将仅限于函数体的范围。

  void somef(std::string str_arg)
  {
     using std::string;
     string str;

     // This is not evil either.    
     using namespace std;
     string str;
  }
  void somef2() {
     //string str; //error
  }

如果somef是类的方法,通常的方法是使用typedef,例如:

class MyClass
{
    typedef std::string string_type;
    //using string_type = std::string; //C++11

    string_type data_member;

    void somef(string_type str)
    {
        string_type local_str;
    }

    void somef2() {
        string_type local_str; // works
    }
};
#include <string>
#include <iostream>
#include "example.hpp"

我会说这也是使用头文件的错误顺序。假设example.hpp使用std::string。这不会引发编译错误,因为您已在<string>之前添加了example.hpp

如果您在其他文件中使用example.hpp而未包含<string>,会发生什么情况呢?您将收到编译错误,因为您的标头使用std::string而您尚未包含std::string

答案 2 :(得分:0)

  
      
  • 我应该在标题 example.hpp 中放置 example.cpp 所需的所有头文件,还是应该将它们保存在示例中。 cpp 就像上面的例子一样?
  •   

将头文件包含在所需的每个文件中。不要担心重复。

  
      
  • C ++在这种情况下如何工作:我在 main.cpp example.cpp 中包含了'string'和'iostream'...是C ++编译器会用字符串和iostream头文件连接两次(或者你会发现更合格的任何术语)程序?我应该只将字符串和iostream标题放在 example.cpp
  • 中   

头文件本身确保其内容仅在任何翻译单元中包含一次。这是通过#ifdef EXAMPLE_H_测试实现的。

  
      
  • 最后,我是否应该在标题本身( example.hpp )或实现中添加我将用于示例标题的命名空间( example.cpp )?
  •   

从不using namespace ...置于头文件永远的全局范围内。

如果您必须使用using namespace ...,请仅在您自己的命名空间或函数中使用它。即使这样,也不建议将它用于大型名称空间,例如std,其中包含每个人一直使用的数千个非常常见的符号。

理想情况下,您应该只在源using namespace ...文件中使用.cpp。但是(如前所述)对于应该在您自己的命名空间中逻辑存在的小命名空间,有时将它包含在标头的非全局部分中是合适的。