【问题标题】:C++ Separate header and source fileC++ 分离头文件和源文件
【发布时间】:2017-06-29 16:18:18
【问题描述】:

我是 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;
            }
        }
    }
};

文本数据.h:

#ifndef SIEDITOR_H
#define SIEDITOR_H

class textData
{
    public: 
        string fileName;
        string appendTextfileName;
        ofstream siFile;
        ofstream siFileAppend;
};

#endif

textEditor.h:

#ifndef SIEDITOR_H
#define SIEDITOR_H

class textEditor
{
    public: 
    void openText()
    void writeText()
    void readText()
    void appendTextOpen()
    void appendText()
};


#endif

【问题讨论】:

  • 你不应该在你的 cpp 文件中重新定义你的类;你应该实现这些方法。理想情况下,您也应该拥有textData.cpptextEditor.cpp,否则我认为它不会被正确包含。
  • 我认为互联网上可能存在将代码拆分为头文件和实现文件的工作示例。

标签: c++ header-files text-editor


【解决方案1】:

您在两个头文件中使用相同的包含保护,即SIEDITOR_H。这可以防止包含第二个标头的内容。使用#pragma once 而不是包含保护符号。

#pragma once 是一个事实上的标准,即supported by all compilers of practical interest

在你的实现文件中不要重复类定义。只需定义声明的成员函数。和static 数据成员,如果有的话。

【讨论】:

    【解决方案2】:

    类只能定义一次。

    将类定义移动到单独的头文件中(将同名类的两个内容合并在一起:字段和方法):

    // textEditor.h
    #pragma once
    class textEditor {
      void appendText();
    private:
        string fileName;
    }
    

    将类方法移动到单独的源文件:

    // textEditor.cpp
    #include "textEditor.h"
    void textEditor::appendText() {
       // ... impl
    }
    

    在main.cpp中:

    // main.cpp
    #include "textEditor.h"
    
    textEditor siEditor;
    
    int main() 
    {
      siEditor.appendText();
    }
    

    【讨论】:

    • “类只能声明一次。” - 不,它们可以根据你的意愿多次声明。它们只能定义一次。 class textEditor { /* ... */ }; 是定义,而不是声明。 class textEditor; 将是一个声明。
    【解决方案3】:

    考虑预处理器的作用。它为每个*.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
    

    这解释了您一直在询问的具体错误消息。解决方案是在每个头文件中使用不同的包含保护。您必须完全选择唯一的包含保护名称。在大型项目中,这可能会变得很困难。以下是一些阅读材料:


    不过,您的代码中还有更多错误:

    首先,你的头文件假设很多。他们假设其他人已经包含了获取std::stringstd::ofstream 所需的标准头文件。他们假设其他人已经使用过using namespace std;using std::string; using std::ofstream;

    这是非常糟糕的做法。您的头文件应包含所需的标准头文件,并且只需拼出完整名称(没有using namespace std;)。

    除此之外,它应该使用保证包含您需要的内容的标准标题。如果您需要std::string,则包括&lt;string&gt;

    标准头文件可能包含其他标准头文件,但只有很少的保证间接包含(如果&lt;iostream&gt;暗示&lt;string&gt;,我懒得在标准中查找,但我猜它没有。)

    这是一个例子:

    textEditor.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;)也不是一个好主意,尤其是考虑到它们没有被包装在匿名命名空间中。


    [*] 实际的预处理器在技术上可能不会像这样工作,但你可以这样想象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-20
      • 1970-01-01
      • 1970-01-01
      • 2016-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      相关资源
      最近更新 更多