TsAihS

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

 

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

 


    

 

posted on 2017-06-07 11:56  TsAihS  阅读(675)  评论(0编辑  收藏  举报