C++分离头文件和源文件
C++ Separate header and source file
我是C++的新手,到目前为止,我已经将所有代码放在同一个文件中。现在,随着我的进展,我需要将我的代码分离到我不太熟悉的源文件和头文件中。
我可以让它与简单的任务一起工作,但在这个程序中,我现在试图将其划分为单独的文件,这会给我一个错误,而当我将所有内容都放在一个文件中时,我可以编译它。
我被错误信息卡住了
main.cpp:10:1: error: unknown type name 'textEditor'
textEditor siEditor;
如果有人能解释我为什么会遇到这个错误,以及如何防止它,我将不胜感激。我读到它可能与重复的声明有关,但我不明白从哪里来的。
这就是我的main.cpp的样子:
#include <iostream>
#include <fstream>
using namespace std;
#include "textData.h"
#include "textEditor.h"
textData siData;
textEditor siEditor;
int main()
{
cout << "nWelcome to siEdit!" << endl;
while (true)
{
cout << "nWhat would you like to do? nNew file = n, Append = a, View = v, Quit = q: ";
string toDo;
cin >> toDo;
if (toDo == "n")
{
siEditor.openText();
cout << "Now editing the file: " << siData.fileName.c_str() << endl;
cout << "Type '=!' to stop editing and save. n " << endl;
siEditor.writeText();
}
else if (toDo == "a")
{
siEditor.appendTextOpen();
cout << "Now appending text: " << siData.appendTextfileName.c_str() << endl;
cout << "Type '=!' to stop editing and save changes. n " << endl;
siEditor.appendText();
}
else if (toDo == "v")
{
siEditor.readText();
cout << "n";
}
else if (toDo == "q")
{
return 0;
}
else
{
cout << "Invalid input." << endl;
}
}
}
siEdit.cpp:
#include <iostream>
#include <fstream>
using namespace std;
#include "textData.h"
#include "textEditor.h"
textData siData;
class textEditor
{
public:
void openText()
{
//when associated file is open.
while (siData.siFile.is_open())
{
siData.siFile.close();
}
cout << "nWhat do you want to call your file? ";
cin >> siData.fileName;
//Creates / Opens fileEditor
const char* path = siData.fileName.c_str();
siData.siFile.open(path);
}
void writeText()
{
bool editing = true;
bool hasEditing = false;
while (editing == true)
{
//Get user input
string input = " ";
getline(cin, input);
string yesNo;
if (input == "=!")
{
cout << "Would you like to save the file? Y/N" << endl;
cin >> yesNo;
if (yesNo == "Y")
{
cout << "Filed saved: " << siData.fileName.c_str();
editing = false;
}
else if (yesNo == "N")
{
cout << "No changes have been saved. Exiting." << endl;
hasEditing = false;
editing = false;
siData.siFile.clear();
}
else
{
cout << "Invalid input. Type '=! to exit." << endl;
}
}
else
{
siData.siFile << input;
siData.siFile << endl;
hasEditing = true;
}
}
}
void readText()
{
string line;
cout << "nEnter the name of your file: ";
cin >> siData.fileName;
cout << "n";
const char* path = siData.fileName.c_str();
// input file stream
//Internal stream buffer which performes I/O on file.
ifstream siFileRead(path);
if(siFileRead.is_open())
{
while(getline(siFileRead,line))
{
cout << line << endl;
siData.siFile << line;
}
}
else
{
cout << "Unable to open file. Confirm name and file location.";
}
}
// open the existing text file
void appendTextOpen()
{
while (siData.siFileAppend.is_open())
{
// erase previous text
siData.siFileAppend.clear();
// close the input text file
siData.siFileAppend.close();
}
cout << "nEnter the name of the file: ";
//find file name.
cin >> siData.appendTextfileName;
//Makes / Opens file
const char* path = siData.appendTextfileName.c_str();
siData.siFileAppend.open(path, fstream::app);
}
//add text together with previous input.
void appendText()
{
bool editing = true;
bool hasEditing = false;
while (editing == true)
{
//Gets user input
string input = " ";
getline(cin, input);
if (input == "=!")
{
if (hasEditing == true)
{
cout << "File saved: " << siData.appendTextfileName.c_str() << endl;
editing = false;
}
}
else
{
siData.siFileAppend << input;
siData.siFileAppend << endl;
hasEditing = true;
}
}
}
};
text数据.h:
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
#endif
text编辑器.h:
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textEditor
{
public:
void openText()
void writeText()
void readText()
void appendTextOpen()
void appendText()
};
#endif
在两个头文件中使用相同的include保护,即SIEDITOR_H
。这样可以防止包含第二个标头的内容。使用#pragma once
而不是包含保护符号。
#pragma once
是一个事实上的标准,所有感兴趣的编译器都支持它。
在实现文件中,不要重复类定义。只需定义已声明的成员函数。以及static
数据成员(如果有的话)。
类只能定义一次。
将类定义移动到分离的头文件(连接到相同名称类的两个内容:字段和方法):
// textEditor.h
#pragma once
class textEditor {
void appendText();
private:
string fileName;
}
将类方法移动到单独的源文件:
// textEditor.cpp
#include "textEditor.h"
void textEditor::appendText() {
// ... impl
}
在主.cpp中:
// main.cpp
#include "textEditor.h"
textEditor siEditor;
int main()
{
siEditor.appendText();
}
考虑预处理器的作用。它分别为每个*.cpp
文件运行,并处理所有#include
、#ifndef
、#define
和#endif
语句。
这是你们的main.cpp
:的开始
#include <iostream> #include <fstream> using namespace std; #include "textData.h" #include "textEditor.h" textData siData; textEditor siEditor;
如果您是预处理器,您将如何预处理它[*]您可能会从#include
语句开始。中间结果是:
// contents of <iostream>...
// contents of <fstream>...
using namespace std;
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
#endif
#ifndef SIEDITOR_H
#define SIEDITOR_H
class textEditor
{
public:
void openText()
void writeText()
void readText()
void appendTextOpen()
void appendText()
};
#endif
textData siData;
textEditor siEditor;
现在让我们检查一下这个中间结果:
// contents of <iostream>...
// contents of <fstream>...
using namespace std;
#ifndef SIEDITOR_H // <--- true, SIEDITOR_H is not defined, don't skip until #endif
#define SIEDITOR_H // <--- OK, SIEDITOR_H is now defined
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
#endif // <--- end of block started by #ifndef SIEDITOR_H
#ifndef SIEDITOR_H // <--- false, SIEDITOR_H is defined, skip until #endif
#define SIEDITOR_H
class textEditor
{
public:
void openText()
void writeText()
void readText()
void appendTextOpen()
void appendText()
};
#endif // <--- end of block started by #ifndef SIEDITOR_H
textData siData;
预处理的结果是:
// contents of <iostream>...
// contents of <fstream>...
using namespace std;
class textData
{
public:
string fileName;
string appendTextfileName;
ofstream siFile;
ofstream siFileAppend;
};
textData siData;
textEditor siEditor; // error, unknown type textEditor
这解释了您询问的特定错误消息。解决方案是在每个头文件中使用不同的include保护。您必须选择完全唯一的包含保护名称。在大型项目中,这可能会变得困难。这里有一些阅读材料:
- 命名包含防护
- #pragma曾经是C++11标准的一部分吗
尽管如此,您的代码中还有更多错误:
首先,您的头文件占用了很多。他们假设其他人已经包含了获得std::string
和std::ofstream
所需的标准头。他们还假设其他人已经使用了using namespace std;
或using std::string; using std::ofstream;
。
这是非常糟糕的做法。头文件应该包含所需的标准头文件,只需拼写完整的名称(无using namespace std;
)。
除此之外,它还应该使用保证包含所需内容的标准标头。如果您需要std::string
,则包括<string>
。
标准标头可能包括其他标准标头,但只有极少数保证的间接包含(如果<iostream>
意味着<string>
,我懒得在标准中查找,但我想它不是。)
这里有一个例子:
text编辑器.h:
#ifndef SI_TEXT_DATA_H
#define SI_TEXT_DATA_H
#include <string>
#include <fstream>
class textData
{
public:
std::string fileName;
std::string appendTextfileName;
std::ofstream siFile;
std::ofstream siFileAppend;
};
#endif
最后,您在siEdit.cpp
中重新定义textEditor
类。这是不允许的。相反,您应该在类定义内部的*.h
文件中声明成员函数,并在*.cpp
文件中定义成员函数。
textEditor.h
应该是这样的:
#ifndef SI_TEXT_EDITOR_H
#define SI_TEXT_EDITOR_H
class textEditor // class definition begins
{
public:
void openText(); // declaration of member function
void writeText(); // declaration of member function
void readText(); // declaration of member function
void appendTextOpen(); // declaration of member function
void appendText(); // declaration of member function
}; // class definition ends
#endif
siEdit.cpp
应该是这样的:
#include "textData.h"
#include "textEditor.h"
textData siData;
void textEditor::openText() // definition of a member function begins
{
// ...
} // definition of a member function ends
// other definitions
全局变量(如textData siData;
)也不是一个好主意,尤其是考虑到它们没有封装在匿名命名空间中。
[*]一个实际的预处理器在技术上可能不会像这样工作,但你可以这样想象
- 为测试目标创建具有不同源文件夹的文件
- 生成一个生成文件,该生成文件使用Automake在一个步骤中编译和链接所有源文件
- 生成文件:动态源文件名和对象目录
- 无法使用 CMake 从其他文件夹添加源文件
- CMake 源文件找不到头文件
- 给定一个源文件,有没有办法要求 gcc 返回仅直接包含的头文件的列表?
- UE4 - Visual Studio在我从编辑器添加新的c ++文件后无法打开任何源文件 - UBT_COMPILED
- cpp 在主源文件中包括.cpp文件导致错误"duplicate symbol"
- 如何将 .ui 完全转换为 C++ 头文件和源文件
- 在生成文件中添加源文件并更新依赖项
- CMake 在源文件附近找不到头文件
- GNU 在看到其他同名文件时重用源文件
- #include "date.h" 创建错误 E1696 无法打开源文件"date.h",也无法打开包含文件:没有这样的文件或目录
- DirectX 11 文件无法打开源文件
- 用libclang解析源文件 - 链接问题包括文件
- 获取对源文件中特定函数的所有调用并生成其他文件(使用 C、C++预处理器或脚本)
- 我有一个预处理的 C/C++ 源文件 (cacti.i).如何从这个 .i 文件生成可执行二进制文件,以便我可以像 ./
- 创建 Matlab MEX 函数时,我是否将 mexFunction 放在 c++ 头文件或源文件中
- Linux 可执行文件中列出的源文件
- 使用GCC的C makefile - 从子文件夹列表中生成源文件列表