2014-09-24 50 views
0

所以我正在用C++編寫一個編譯器。目前在掃描儀部分。C++方法返回指向抽象類的指針,需要使用子類中的方法

掃描儀內的方法聲明是

Token * Scanner::next_token() 
{ 
    string * test = new string("Test"); 
    IdToken * testToken = new IdToken(test); 
    return testToken; 
} 

的IdToken類有一個方法get_attribute()返回私有變量ATTR(在這種情況下是一個字符串的值,所述一個傳遞在創建)。令牌(抽象類)沒有這種方法。

裏面的主我的測試中我有這樣的:

IdToken * testToken = testScanner->next_token(); 

但G ++不喜歡這一點,說,這是從令牌*至* IdToken一個無效的轉換。

我需要將該方法返回的標記轉換爲IdToken以獲取屬性,因爲當我嘗試直接調用get_attribute()對返回的標記時,它告訴我Token :: get_attribute()不存在。

由於我對C++繼承的知識等等,我不確定如何去解決這個問題。我做了所有我能做的研究,但是我找不到任何我都瞭解並解決了我的問題的東西。

這裏是Scanner.cc

Scanner::Scanner (char * filename) 
{ 
    buf = new Buffer(filename); 
} 

//Destroy new things 
Scanner::~Scanner() 
{ 
    delete buf; 
} 

//The huge DFA turned into code 
Token * Scanner::next_token() 
{ 
    string * test = new string("Test"); 
    IdToken * testToken = new IdToken(test); 
    return testToken; 
} 

這裏是IdToken.cc

IdToken::IdToken() : Token() 
{ 
    set_token_type (TOKEN_ID); 
    attribute = new string("UNINITIALIZED IDENTIFIER ATTRIBUTE"); 
} 

IdToken::IdToken (string *attr) : Token() 
{ 
    set_token_type (TOKEN_ID); 
    attribute = new string(*attr); 
} 

IdToken::~IdToken() 
{ if (attribute != NULL) { 
    delete attribute; 
    } 
} 


string *IdToken::get_attribute() const 
{ 
    string *attr = new string(*attribute); 
    return attr; 
} 


void IdToken::set_attribute(string *attr) 
{ 
    if (attribute != NULL) { 
    delete attribute; 
    } 
    attribute = new string (*attr); 
} 


string *IdToken::to_string() 
{ 
    string *attribute_name = new string ("ID:" + *attribute); 
    return attribute_name; 
}  

而且最後token.cc

#include "token.h" 

Token::Token() 
{ 
    type = TOKEN_NO_TYPE; 
} 

Token::~Token() 
{} 

void Token::set_token_type (token_type_type type) 
{ 
    this->type = type; 
} 

token_type_type Token::get_token_type() const 
{ 
    return type; 
} 

它幾乎沒有做,我只是需要幫助弄清楚如何訪問get_attribute。

+3

自從我和編譯器一起工作以來,我提出了一個很好的建議...爲什麼不先學習C++和**然後嘗試編寫玩具編譯器?而對於這裏發佈的問題IdToken和令牌代碼可以極大地幫助 – 2014-09-24 21:32:02

+0

我會相應地編輯。這是一個班級。我很瞭解C++,但是我沒有處理任何與繼承有關的事情,當然也不是這個功能。 我將編輯帖子並添加代碼。 – Neal 2014-09-24 21:33:48

+2

我同意Marco的意見。但是,我不清楚爲什麼你需要知道那是什麼樣的標記。答案是要麼不關心它在這個時候是什麼類型的標記,要麼可能使用'dynamic_cast'(或者如果你使用'llvm'作爲後端的''llvm :: dyn_cast') – 2014-09-24 21:38:20

回答

1

兩個選擇:

  1. Token創建virtual成員函數。

    virtual std::string get_attribute() const = 0; 
    

    適用於Token的子類別。使用它作爲:

    Token * testToken = testScanner->next_token(); 
    std::string attr = testToken->get_attribute(); 
    
  2. 使用dynamic_castToken*得到IdToken*。如果演員成功,請致電get_attribute()。在IdToken*

    Token * testToken = testScanner->next_token(); 
    IdToken * testIdToken = dynamic_cast<IdToken*>(testToken); 
    if (testIdToken) 
    { 
        std::string attr = testIdToken->get_attribute(); 
    } 
    
+0

在這種情況下動態鑄造良好的編碼實踐?我從來沒有使用過,我有點困惑。返回的指針是一個指向IdToken對象的指針,但編譯器只將它看作指向Token的指針。 – Neal 2014-09-24 21:40:38

+1

@Neal如果必須使用它,但使用基本類型通常是更好的做法 – 2014-09-24 21:42:51

+0

好的。我明天可以問我的教授,但我想通過互聯網環顧四周不會受到傷害。他是給了我們Scanner.h的人,它在我的Scanner.cc中聲明瞭它,所以我不知道該怎麼去做。 – Neal 2014-09-24 21:44:10

0

您可以使用dynamic_cast

struct Token { 
    virtual ~Token() {} 
}; 

struct IdToken : public Token { 
    int getAttribute() {return 1;} 
}; 

int main(int argc, char** argv} { 

    Token* token = new IdToken(); 

    dynamic_cast<IdToken*>(token)->getAttribute(); 

    delete token; 
} 

不幸的是,dynamic_cast往往是相當緩慢的,你可能會想,以避免對其頻繁調用。然而,這是安全的。它在失敗時返回nullptr。您也可以使用reinterpret_cast,這更快,但並不安全。

0

我個人不會做出了令牌的類層次結構。有一小部分屬性和參數,如果你真的必須的話,你可能會使用聯合來存儲它們。

但如果必須,則使用dynamic_cast打電話給你get_attribute

Token* token = testScanner->next_token() 

IdToken *idToken = dynamic_cast<IdToken*>(token); 
if(idToken) 
{ 
    idToken->get_attribute(); 
} 

請注意,你需要的if,或者你的程序將崩潰,如果你得到了令牌不是一個IdToken [或派生從IdToken]。

呵呵,dynamic_cast不是一個簡單的操作,根本不需要時間。所以避免它偏愛基礎級虛擬功能幾乎總是更好。我的編譯器使用幾十個llvm::dyn_cast作爲解析器出來的AST,因爲要完成一個完全通用的AST類,它可以處理變量表達式的所有特殊情況,同一類中的for循環和函數聲明會使一個擁有幾十個虛擬函數的類的怪物,其中大多數需要單個派生類的單個實現,但對於大多數其他類需要「null」值 - 而且大多數時候我需要知道它是什麼類其實是無論如何...

這是我的Token class - 我敢肯定,那裏也有錯誤,我不是一個編譯器專家,這是我的第三語言,我第一個實際編譯到機器代碼,我通過使用llvm作爲後端作弊。

不要做string *something = new string;使用空或「未知」字符串來表示「尚未設置」

也不要使用if (pointer) delete pointer; - delete作品就好在是NULL指針 - 冗餘,如果可能不會被編譯器刪除,因爲在極少數情況下,調用delete的額外開銷值得保存 - 但不是在析構函數中。如果在大型項目中隨處可見額外的if (pointer),那麼它很快就會累加多達數千字節的額外代碼 - 如果析構函數恰好被內聯,則會乘以內聯的數量,這可能相當多無用的代碼在你的項目中。編譯器代碼往往變得足夠大而沒有無用的膨脹。

相關問題