递归头文件依赖
互相依赖的对象
最近看代码的时候,见到如下骨骼精奇的代码,两个类互相引用,却并没有通过指针和引用的方式。
首先是array.h
,里面Array
引用Node
:
#pragma once
#include <vector>
using namespace std;
class Node;
class Array
{
public:
Array()
{
}
template <typename T>
Array(vector<T>& _data);
Node GetValue(size_t index) const;
};
#include "array.hpp"
然后是node.h
, 里面Node
引用Array
:
#pragma once
#include <vector>
using namespace std;
class Array;
class Node
{
public:
Node();
template<typename T>
Node(vector<T>& _data);
Array GetArray() const;
};
#include "node.hpp"
按照常理,我们使用前向声明的类的时候,只能使用前向声明的类的指针或者引用。如果直接传递类对象,同一个翻译单元内找不到这个前向声明类的定义的时候,会编译不通过,提示无法使用不完整的类型。但是下面的代码是可以通过编译并运行的
#include "array.h"
#include <iostream>
int main(void)
{
auto temp = Array();
temp.GetValue(0);
std::cout<<"wow"<<std::endl;
}
同樣的,下面的代码也是可以通过编译并运行的:
#include "node.h"
#include <iostream>
int main(void)
{
auto temp = Node();
temp.GetArray();
std::cout<<"wow"<<std::endl;
}
互相include的头文件
能编译通过的核心就在这两个头文件末尾include
的相关文件里,这里我把相关文件的详细内容都贴一下:
node.h
包含了node.hpp
#pragma once
#include "array_node_container.h"
template<typename T>
Node::Node(vector<T>& data)
{
}
node.hpp
包含了array_node_container.h
#pragma once
class Array;
template<typename T>
class ArrayNodeContainer
{
public:
ArrayNodeContainer()
{
}
Array GetArray() const;
};
#include "array_node_container.hpp"
array_node_container.hpp
包含了array.h
#pragma once
#include "array.h"
template<typename T>
Array ArrayNodeContainer<T>::GetArray() const
{
return Array();
}
array.h
依赖于array.hpp
, 而array.hpp
依赖于array_container.h
#pragma once
#include "array_container.h"
template<typename T>
Array::Array(vector<T>& _in_data)
{
}
array_container.h
依赖于array_container.hpp
#pragma once
class Node;
template<typename T>
class ArrayContainer
{
public:
ArrayContainer(vector<T>& a);
Node GetValue(size_t index);
vector<T> _Array;
};
#include "array_container.hpp"
array_container.hpp
依赖于node.h
#pragma once
#include "node.h"
template <typename T>
ArrayContainer<T>::ArrayContainer(vector<T>& a)
{
_Array = a;
}
template <typename T>
Node ArrayContainer<T>::GetValue(size_t index)
{
return {_Array[index]};
}
上述的hpp
文件都是用来定义类内部的模板函数,实现了一定程度上的声明与定义相分离原则。
另外还有两个简单的cpp
文件,array.cpp
和node.cpp
,基本就是简单实现一下非模板接口。
array.cpp
#include "array.h"
Node Array::GetValue(size_t index) const
{
return Node();
}
node.cpp
#include "node.h"
Node::Node()
{
}
Array Node::GetArray() const
{
return Array();
}
至此我们跟踪node.h
的所有依赖头文件,最终会依赖到自身node.h
。
- node.h
- node.hpp
- array_node_container.h
- array_node_container.hpp
- array.h
- array.hpp
- array_container.h
- array_container.hpp
- node.h
而单独跟踪array.h
的依赖路径,也会产生类似的递归依赖:
- array.h
- array.hpp
- array_container.h
- array_container.hpp
- node.h
- node.hpp
- array_node_container.h
- array_node_container.hpp
- array.h
头文件展开
我们可以通过gcc/clang
的-E -P
命令来处理这两个cpp
文件,来查看最后生成的头文件展开结果。
clang -E -P node.cpp > expand_node.txt
的结果太长,我就简单介绍一下最终展开结果中上述头文件正文内容的插入顺序:
- node.h
- array_node_container.h
- array.h
- array_container.h
- array_container.hpp
- array.hpp
- array_node_container.hpp
- node.hpp
- node.cpp
类似的clang -E -P array.cpp > expand_array.txt
的结果如下:
- array.h
- array_container.h
- node.h
- array_node_container.h
- array_node_container.hpp
- node.hpp
- array_container.hpp
- array.hpp
- array.cpp
之前我们看到了node.h
和array.h
是互相依赖的,按照常理展开的时候会递归展开,从而导致无限递归爆栈。阻止这个情况发生依赖于#pragma once
这个声明, 从而起到了python
的import
的效果,引入一个头文件的时候会记录这个头文件已经被引入了,后续的引入检查这个标记,如果已经被引入则不做任何操作。除了#pragma once
之外,等价的还有形式为#ifndef __H_ARRAY_H__
的header guard
。
手工展开hpp文件
之前的代码看上去很完美,用极其诡异的方式构造了互相引用。个人觉得里面的那个hpp
文件比较多余,因此动歪脑筋想手动展开所有的hpp
文件到h
文件里面,结果如下。
array.h
#pragma once
#include <vector>
#include "array_container.h"
using namespace std;
class Node;
class Array
{
public:
Array()
{
}
template <typename T>
Array(vector<T>& _data);
Node GetValue(size_t index) const;
};
template<typename T>
Array::Array(vector<T>& _in_data)
{
}
array_container.h
#pragma once
#include <vector>
#include "node.h"
class Node;
using namespace std;
template<typename T>
class ArrayContainer
{
public:
ArrayContainer(vector<T>& a);
Node GetValue(size_t index);
vector<T> _Array;
};
template <typename T>
ArrayContainer<T>::ArrayContainer(vector<T>& a)
{
_Array = a;
}
template <typename T>
Node ArrayContainer<T>::GetValue(size_t index)
{
return { _Array[index] };
}
array_node_container.h
#pragma once
#include "array.h"
class Array;
template<typename T>
class ArrayNodeContainer
{
public:
ArrayNodeContainer()
{
}
Array GetArray() const;
};
template<typename T>
Array ArrayNodeContainer<T>::GetArray() const
{
return Array();
}
node.h
#pragma once
#include <vector>
#include "array_node_container.h"
using namespace std;
class Array;
class Node
{
public:
Node();
template<typename T>
Node(vector<T>& _data);
Array GetArray() const;
};
template<typename T>
Node::Node(vector<T>& data)
{
}
其他的cpp
文件都不变,执行clang ./main.cpp ./array.cpp ./node.cpp
,是可以正常生成可执行文件并运行的。
可是msvc
他不同意啊。。。。。。。
1>array.cpp
array_node_container.h(17): error C2027: 使用了未定义类型“Array”
array_node_container.h(3): note: 参见“Array”的声明
array_node_container.h(18): error C2027: 使用了未定义类型“Array”
array_node_container.h(3): note: 参见“Array”的声明
1>main.cpp
array_node_container.h(17): error C2027: 使用了未定义类型“Array”
array_node_container.h(3): note: 参见“Array”的声明
array_node_container.h(18): error C2027: 使用了未定义类型“Array”
array_node_container.h(3): note: 参见“Array”的声明
1>node.cpp
array_container.h(23): error C2027: 使用了未定义类型“Node”
array_container.h(4): note: 参见“Node”的声明
1>正在生成代码...
1>已完成生成项目“test_recursive.vcxproj”的操作 - 失败。
求各位路过的行行好指导一下如何改展开hpp
之后的文件,让项目编译通过吧,卡这快一个礼拜了。