2017-02-11 78 views
0

我是C++的新手,到目前爲止我已經將所有代碼放在同一個文件中。 現在,隨着我的進步,我需要將我的代碼分離成我並不熟悉的源代碼和頭文件。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; 
      } 
     } 
    } 
}; 

textData.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 
+0

您不應該在您的cpp文件中重新定義您的類;你應該實施這些方法。理想情況下,你應該有'textData.cpp'和'textEditor.cpp',否則我不認爲它會被正確包含。 – byxor

+0

我認爲可能有工作示例的代碼拆分爲網頁上的標題和實現文件。 – juanchopanza

回答

1

類只能定義一次。

移動類定義到分離的頭文件(加入togather你同名類的兩個內容:字段和方法):

// 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(); 
} 
+0

*「類必須只聲明一次。」* - 不,它們可以按照您的意願經常聲明。他們必須**定義**只有一次。 'class textEditor {/ * ... */ };'是一個定義,而不是一個聲明。 'class textEditor;'是一個聲明。 –

+0

當然可以。謝謝,修復。 – oklas

2

在頭文件f中使用相同的include guard iles,即SIEDITOR_H。這可以防止包含第二個標題的內容。使用#pragma once而不是包含防護符號。

#pragma once事實上標準是supported by all compilers of practical interest

在您的實現文件中不要重複類定義。只需定義聲明的成員函數。和static數據成員,如果有的話。

0

考慮預處理器的功能。它對每個*.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;

這是非常糟糕的做法。你的頭文件應該包含它需要的標準頭文件,只需要拼寫完整名稱(no using namespace std;)。

除此之外,它應該使用標準頭這是保證包含你所需要的。如果您需要std::string,則包括<string>

標準頭可以包括其他標準的頭,但只有極少數的保證間接夾雜物(我懶得查找標準,如果<iostream>意味着<string>,但我想沒有。)

下面是一個例子:

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 

最後,你重新定義你的textEditorsiEdit.cpp。這是不允許的。您應該在類定義中的*.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;)是不是一個很好的主意,或者說,特別是看到它們如何不被包裝在匿名命名空間中。


[*] 實際的預處理程序可能無法正常工作在技術上是這樣,但你能想象這種方式。