问题阐述
写图的数据结构相关代码时,本来在同一个文件里面写了MatGraph类(图的邻接矩阵)、AdjGraph类(图的邻接表),一些图相关的算法实现和main函数。但后来认为将所有代码堆在同一个文件里不是一个好的程序结构,结构不清晰也不利于后期维护和回顾。因此准备将两个类的成员变量定义、成员函数声明分别放入.h文件中,将成员函数实现放入.cpp中,再在test.cpp中编写main函数,创建对象和调用函数。
但是真正实施的时候出现了重定义的问题,搜索各种资料尝试各种方法,搞了一个上午也没能解决。最终通过增加另一个.h文件专门用来定义常量,作业跑通了程序。在此记录一下解决问题的过程。
或许这仍然不是一个最优解决方案,希望可以有大佬指点~
我在程序中定义了两个常量MAXV和INF,分别表示图中顶点数的最大值和无穷。
const int MAXV = 100;
const int INF = 0x3f3f3f3f;
同时,两个类中都定义了容量为MAXV的数组。
//MatGraph.h文件
class MatGraph {//邻接矩阵类
public:
int edges[MAXV][MAXV];
int n, e;
//创建图的邻接矩阵
void CreateMatGraph(int a[][MAXV], int n, int e);
//输出图
void DispMatGraph();
//以传入的节点号为起点广度优先遍历
//时间复杂度为0(n+e)
void MatGraph_BFS(int f);
};
//AdjGraph.h文件
struct ArcNode {//边节点
int adjvex;
int weight;
ArcNode* nextarc;
};
struct HNode {//头结点
string info;
ArcNode* firstarc;
};
class AdjGraph {//邻接表类
public:
HNode adjlist[MAXV];
int n, e;//顶点数,边数
//构造函数
AdjGraph();
//析构函数,释放邻接表指针空间
~AdjGraph();
//通过a、n、e创建图的邻接表
void CreateAdjGraph(int a[][MAXV], int n, int e);
//输出图
void DispAdjGraph();
//以传入的节点号为起点深度优先遍历
int visited[MAXV] = { 0 };
void AdjGraph_DFS(int f);
};
如果两个.h文件中同时定义常量MAXV和INF,则会报重定义的错误:
解决方式一(不适用)
在网上看见可以通过extern声明外部变量。
C99中规定 :所有顶层的默认存储类标志符都是extern。
也就是extern关键字写不写都可以,因此我在MatGraph.h文件中定义了两个常量并赋初值,在AdjGraph.h文件中声明,但没有赋初值。希望编译器可以顺利连接。
但是由于C++语言规定数组长度在编译时必须有明确的值,即必须在数组定义时指定数组的长度;指定数组长度的常量表达式只能是整型字面常量或符号常量。
而在编译器连接时似乎认为AdjGraph.h中的MAXV没有被初始化,会报如下错误:
解决方式二
我尝试只在MatGraph.h文件中定义两个常量并赋初值,而在AdjGraph.h中#include “MatGraph.h”。
此时报出了如下错误:
这是因为我在test.cpp中#include “MatGraph.h”并#include “AdjGraph.h”,这样MatGraph.h就被嵌套包含,被编译多次,于是编译器报重定义的错误。
重复编译解决方式一
解决方式是在MatGraph.h的开头加上这句话:
#pragma once
意思就是这个程序无论被包含多少次,只编译一次,从而解决了重定义问题。
重复编译解决方式二
同样可以用如下预编译指令解决:
#ifndef A_H
#define A_H
//A.h的内容
#endif
意思是如果未定义A_H,就定义它;如果已定义就什么都不干。
这里A_H的标识符可以自定义。
上面两种方式的作用一样,编写.h文件时加上这几句话是一个良好的编程习惯。
虽然问题解决了,但是我仍然认为.h包含.h的方式不是特别好,因为MatGraph和AdjGraph是两个地位平等的类,并不存在一个包含另一个的关系,我却为了常量的共用而破坏了这种平等关系,程序结构不太符合逻辑。
解决方式三
我重新写了一个CONST.h,专门用来存放两个常量的定义,并在MatGraph.h和AdjGraph.h中#include “CONST.h”,程序也跑通啦!
这样我就没有破坏两个类之间的平等关系,同时实现了常量的共用~