2014-09-23 62 views
0

我在思考如何在C++中構造程序時遇到了一些麻煩。我正在學習一本C++的書,並且一度構建兩個類來解決問題。這本書最終將兩個類,以及它們如何在一個文件中全部使用並運行它,這是有效的。但我知道更合適的結構化代碼將包括頭文件,每個類將獲得它自己的文件,並試圖像這樣構造程序會導致問題,當我試圖獲得代碼編譯。C++程序結構

我有兩個類,Token和Token_Stream,我知道Token和Token_Stream應該有自己的文件,並且每個都應該有一個聲明文件。我的主要問題是:

Token_Stream需要了解令牌。當Token_Stream被初始化時,它初始化一個Token。我曾認爲只需在Token_Stream中包含Token的聲明就足夠了,但似乎並非如此。我知道一些關於用OOP語言進行編程的知識,但Token_Stream不會從Token繼承任何東西,也不應該(它相信)它只需要足夠的知識來初始化Token並存儲它。我將包括各相關文件如下:

Token.h

// Token.h, declaration for Token 

class Token 
{ 
public: 
    char kind; 
    double value; 

    Token(char ch); 

    Token(char ch, double val); 
}; //class Token 

Token.cpp

// Token.cpp 

#include "Token.h" 

using namespace std; 

Token::Token(char ch) 
    :kind(ch), value(0){} 

Token::Token(char ch, double val) 
    :kind(ch), value(val) {} 

Token_Stream.h

// Token_Stream.h, declarations 


class Token_Stream 
{ 
public: 
    Token_Stream(); 
    Token get(); 
    void putback(Token); 

private: 
    bool full; // do we already hold a token? 
    Token buffer; // what Token do we hold? 
};//class Token_Stream 

Token_Stream.cpp

// Token_Stream.cpp implementation. 

#include <iostream> 
#include <stdexcept> 
#include "Token.h" // needs to know what a Token is 
#include "Token_Stream.h" 

using namespace std; 

/*********************************************************************** 
* Token_Stream::Token_Stream() 
* 
* Constructor for Token_Stream(), sets full = false and buffer as 0 
* Need to do :buffer(0), so we don't create an extra buffer variable 
**********************************************************************/ 
Token_Stream::Token_Stream() 
:buffer(0) 
{ 
    full = false; // nothing in our stream yet. 
}//constructor 

/*********************************************************************** 
* void Token_Stream::put_back(Token t) 
* 
* Given a token, we fill buffer and change full to true 
* 
* Parameter: t - Token to fill buffer 
**********************************************************************/ 
void Token_Stream::putback(Token t) 
{ 
    if(!full) // if its empty 
    { 
     buffer = t; 
     full = true; 
    }//if not full 
    else 
     throw runtime_error("buffer already full"); 
}// putback 


/*********************************************************************** 
* Token Token_Stream::get() 
* 
* gets another token from input, or if we have one stored, gets that. 
* 
* Returns: Token - next token in stream, either from buffer or from 
*     input 
**********************************************************************/ 
Token Token_Stream::get() 
{ 
    if(full) //if we already have something 
    { 
     full = false; 
     return buffer; 
    } 

    //if we've reached here we haven't returned: 

    char ch; 
    cin>>ch; //get next input and switch over cases: 
    switch(ch) 
    { 
     // if they input a valid character: 
     case ';': 
     case 'q': 
     case '(': case '+': case '*': case '-': case '/': case '%': 
     case ')': 
      return Token(ch); 
      break; 

     //if they input a valid number, or lead with a decimal i.e., .5 
     case '.': case '0': case '1': case '2': case '3': case '4': 
     case '5': case '6': case '7': case '8': case '9': 
     { 
      cin.putback(ch); 
      double val; 
      cin>>val; //read it as a number 
      return Token('8',val); 
      break; 
     }//case of valid number 
     default: 
      throw runtime_error("Bad Token"); 
    }//switch 
}//get 

所以這些是文件,當我嘗試編譯東西,即在Token.cpp中放置一個空的int main(){}時,一切工作正常,我編譯,如果我想我可以運行的東西main()的

但是當我試圖把一個空白INT的main(){}在Token_Stream.cpp並嘗試編譯它不工作,我運行:

g++ -Wall -std=c++11 -o "Token_Stream" "Token_Stream.cpp" 

,我也不讓行號錯誤,但它聲稱對Token :: Token(char)等的未定義引用以及令牌構造函數的其餘部分,所以我猜這意味着Token_Stream.cpp需要看到更多的Token.cpp,怎麼做我這樣做?我是否同時編譯它們?

+1

您不需要將自己限制爲每個翻譯單元或頭文件一個類。在一個頭文件中有幾個類是很常見的。 – 2014-09-23 19:01:33

+0

緊密相關的類可以保存在同一個文件中。的 – Barmar 2014-09-23 19:02:02

+0

可能重複[未定義引用(http://stackoverflow.com/questions/5293021/undefined-reference-to) – Borgleader 2014-09-23 19:03:00

回答

2

您需要將Token.cpp鏈接到您的可執行文件。

g++ -Wall -std=c++11 -o "Token_Stream" "Token.cpp" "Token_Stream.cpp"

否則GCC不會找到令牌的構造函數的實現。

+0

啊!好吧,這確實讓我編譯!謝謝。 所以這個想法是,如果你有另一個文件的實現,你也必須把它給你的編譯器,編譯器只是解決依賴關係?或者訂單在這裏重要嗎? – user2386276 2014-09-23 19:16:18

+0

@ user2386276:實際上它是_linker_,而不是編譯器,在做這些事情。是的,鏈接器爲您解決了這些依賴關係(對於大多數文章而言)。該命令可能很重要,但我暫時不會擔心;它更多的是與'-l'標誌鏈接的第三方庫的問題。當然是 – 2014-09-23 19:17:38

+0

。是的-o意味着鏈接器,對嗎? – user2386276 2014-09-23 19:19:20

0

除了你的鏈接問題,如果你正在程序中尋找合適的結構,你仍然需要整理你的依賴關係。

Token_Stream.h應該這樣開始:

// Token_Stream.h, declarations 
#include "Token.h" // Note that this include is at the top. 

class Token_Stream 
... 

Token_Stream。CPP應該開始這樣的:

// Token_Stream.cpp implementation. 
#include "Token_Stream.h" // Note that this include is at the top. 

#include <iostream> 
#include <stdexcept> 
... 

主要以上幾點:

  1. 每個頭文件應當包括在它的實現文件的頂部。
  2. 每個頭文件都應該包含它實際需要的所有東西,而不是更多。

這些先決條件將允許您的客戶將您的標題包含在他們需要的任何地方。