zoukankan      html  css  js  c++  java
  • 头文件与源文件

     以下内容全部是个人总结,如果有错误请指正!

    在初学C++的时候,我总是彷徨于不恰当使用头文件、声明、定义、源文件而导致的各种Link错误。今天我想通过一些简单的测试,得到一些概括的,一般性的结论。(因为我没有学习过C++的编译器,所以我所以这些结论仅仅是一些根据一些现象的猜想)

    实验环境:集成开发环境(Visual Studio 2017),并没有直接使用过g++的shell命令。

    1. 在Visual Studio 2017 环境下,项目中的文件会被赋予不同的类型每个类型有各自的作用。

    1.1 头文件

      头文件不会参与Link过程,所以头文件中如果存在语法错误,是不会被发现的。

    e.g:

    在头文件文件夹下新建文件:输入“wrong code”。运行 源.cpp,发现正常运行。

    1.2 源文件

      源文件既可以被 #include 导入(因为头文件源文件类型的区分是VS的限定,但对于一个编译器来说任何文件都是没有区别的,只有输入的参数不同),但导入之后同样会编译自身之后Link。

    e.g:

    在源文件文件夹下新建文件a.h: 定义函数 a, 在源.cpp 中include该文件,并使用a函数。

    系统将出现符号重定义的错误:因为 VS认为a.h是源文件故编译了他。而源.cpp中include的部分也被编译,故出现了两个相同的符号。 

    2. C++ 的 #include 对文件的名字(后缀)不敏感

    2.1 可以使用无 .h 后缀的文件作为 #include 的目标

      这个是一个不需要实验的结论,因为常用的#include <iostream>便是一个很好的例子。

    2.2 如果把一个正常的 .h 文件改成 .cpp 后缀,效果是一样的


    正常情况:

    // 源.cpp
    #include "A" #include <iostream> int main() { A a; //int main2(); std::cout << "在main里:" << a.value << std::endl; //std::cout << "在main里:" << a.value << " " << a.functionA() << std::endl; //std::cout << "在main2里:"; //main2(); getwchar(); return 1; }
    // A
    #pragma
    once class A { public: int value = 100; //A(); //~A(); int functionA(); private: };

    正常输出


    当 #include 的对象改为 .cpp 后缀

    // 源.cpp
    #include "A.cpp" #include <iostream> int main() { A a; //int main2(); std::cout << "在main里:" << a.value << std::endl; //std::cout << "在main里:" << a.value << " " << a.functionA() << std::endl; //std::cout << "在main2里:"; //main2(); getwchar(); return 1; }
    // A.cpp
    #pragma
    once class A { public: int value = 100; //A(); //~A(); int functionA(); private: };

    正常输出

    3. #include 是的本质是将 #include文件的全体代码替换到当前位置

    3.1 (猜想)因为 #include 带 “#” 所以是预处理过程,其过程将先与其他语法检查步骤


    使用 #include 语句

    // 源.cpp
    #include "testInclude.h" #include <iostream> int main() { std::cout << functionToTestInclude(); getwchar(); return 1; }
    // testInclude.h
    #pragma
    once int functionToTestInclude() { return 1; }

    正常输出


    直接将 #include 对象替换掉 #include 语句

    // 源.cpp
    int
    functionToTestInclude() { return 1; } #include <iostream> int main() { std::cout << functionToTestInclude(); getwchar(); return 1; }

    正常输出

    结论:

    1. 替换后效果一样。

    2. 没有检测出“未定义函数”,说明语法检测过程是在 #include 之后。

    3. 所以现在 “#include 的文件” 也没有必要讨论了。我们所讨论的项目里剩下的,#include 别的文件的文件,并默认在 #include 展开之后,这些文件就是Visual Studio 2017里标记类型为源文件的需要被编译的文件。

    4. 单个文件内支持多次全局声明

    4.1 多个文件支持重复全局声明

      const部分

      C++中的声明与定义


    // 源.cpp
    //
    声明函数 int functionToTestInclude(); int functionToTestInclude(); int functionToTestInclude(); // 声明变量 extern int intV; extern int intV; // 声明函数指针 int *intFP(); int *intFP(); class classExample; class classExample; // 正常运行 int main() { return 1; }
    // 源1.cpp
    //
    声明函数 int functionToTestInclude(); int functionToTestInclude(); int functionToTestInclude(); //声明变量 extern int intV; extern int intV; // 声明函数指针 int *intFP(); int *intFP(); class classExample; class classExample;

    5. 单个文件中不允许任何全局数据重复定义,只有类允许在多个文件中重复定义(包括类内方法定义,但不包括类外部实现的方法)

    5.1 使用函数时,可以先声明,编译器会自动查找到函数的定义(在后文或其他文件内)。

    5.2 定义对象时,必须在之前上文中有类的定义。

    #include "iostream"
    
    class testClass;
    class testClass
    {
    };
    int function();
    
    int main() {
    
        testClass as;
        int ret = function();
        getchar();
        return 1;
    
    }
    // 函数在后文定义不会出现问题
    int function() { return 1; }
    // 将testClass的定义 替换到此处,将导致程序非法,出现 “testClass”未定义的错误。

    5.3 定义类时,如果需要某个其它类构造器内部的属性或方法,可以不用定义,声明即可,但必须在同一文件内。

    #include "iostream"
    
    class testClass;
    class refClass;
    class testClass
    {
    public :
        refClass ref_class;
    };
    
    
    int main() {
    
        testClass as;
        getchar();
        return 1;
    
    }
    int function() { return 1; }
    
    // 先声明,在后文定义不会出现问题 (若将此定义移至其他文件,将会出现“refClass未定义”的错误)
    class refClass { };

    5.4 多个文件重复定义类,构造对象时所选用的类定义是不确定的。

    5.4.1 因为一般情况类写在头文件内,所以每个文件 include 展开后类的定义是相同的,故不会出现问题。


        

  • 相关阅读:
    使用数组实现简单线性表功能
    解析.NET 许可证编译器 (Lc.exe) 的原理与源代码剖析
    Entity Framework with NOLOCK
    64位CentOS 6.0下搭建LAMP环境
    如何正确看待Linq的DistinctBy扩展和ForEach扩展
    jQuery最佳实践
    大话数据结构-树
    hdu2534-Score
    WKE——Webkit精简的纯C接口的浏览器
    WM_ERASEBKGND官方解释(翻译),以及Delphi里所有的使用情况(就是绘制窗口控件背景色,并阻止进一步传递消息)
  • 原文地址:https://www.cnblogs.com/TsAihS/p/6955219.html
Copyright © 2011-2022 走看看