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 展开后类的定义是相同的,故不会出现问题。


        

  • 相关阅读:
    Yield Usage Understanding
    Deadclock on calling async methond
    How to generate file name according to datetime in bat command
    Run Unit API Testing Which Was Distributed To Multiple Test Agents
    druid的关键参数+数据库连接池运行原理
    修改idea打开新窗口的默认配置
    spring boot -thymeleaf-url
    @pathvariable和@RequestParam的区别
    spring boot -thymeleaf-域对象操作
    spring boot -thymeleaf-遍历list和map
  • 原文地址:https://www.cnblogs.com/TsAihS/p/6955219.html
Copyright © 2011-2022 走看看