簡體   English   中英

C ++ - 好的或壞的做法?

[英]C++ - Good or bad practice?

我正面臨着這樣的情況:我發現在基類中存儲對象的類型(作為枚舉)以進一步將指向該基類的指針轉換為子類指針(取決於該類型的值)。

例如 :

class CToken
{
public:
   token_type_e eType;
};

class COperatorToken : public CToken
{
public:
    // Sub class specific stuff
};

class CLiteralToken : public CToken
{
public:
    // Sub class specific stuff
};

接着

vector<CToken *> aTokens;

//...

for( size_t nI = 0, nMaxI = aTokens.size(); nI < nMaxI; ++nI )
{
   switch( aTokens[ nI ]->eType )
   {
   case E_OPERATOR :
      // Do something with sub-class specific stuff.
      break;
   case E_LITERAL :
      // Do something with sub-class specific stuff.
      break;
   }
}

這是一種不好的做法嗎?

謝謝 :)

編輯:

說我正在分析我的令牌列表。 在某些時候,我想要檢查當前令牌是否是一個操作員,正如人們所建議的那樣,使用虛擬函數virtual bool isOperator() 現在,如果它是一個運算符,我將要訪問該子類特定的東西,以找出,例如,它是哪種類型的運算符。 在那種情況下,我該怎么辦? 我無法在我的基類中添加方法getOperatorType(),這是沒有意義的。 除了轉換為子類以檢索該子類成員值之外,還有其他方法嗎?

類型字段確實破壞了C ++的面向對象特性。 通常你可以用多態來解決這個問題:

CToken定義一個函數virtual doSomething() ,帶有適當的參數和返回類型。

COperatorTokenCLiteralToken實現該功能。

如果你使用aTokens[ nI ]->doSomething();運行時將調用相應的函數aTokens[ nI ]->doSomething();

聽起來像是在嘗試模擬代數數據類型。 通常,您不會這樣做,而是通過在基類上放置純virtual函數並在派生類的覆蓋中實現實際行為。

此外,如果您確實需要您提出的模式,語言運行庫會知道類型,因此您無需存儲它:

#include <iostream>
#include <typeinfo>

class Token { };

class Operator : public Token { };

class Literal : public Token { };

int main()
{
    Token *tok = new Literal();

    if (typeid(*tok) == typeid(Literal)) {
        std::cout << "got a literal\n";
    }
    else if (typeid(*tok) == typeid(Operator)) {
        std::cout << "got an operator\n";
    }
}

我會問一個不同的問題:你為什么要重新發明輪子而不是使用現有的可能性,即虛擬成員(多態)? 因此,如果沒有一些強有力的理由這樣做,我會稱之為不好的做法。

您可以重載虛擬成員,即使您的指針是基類,您仍然會調用實際(子)類的成員(請注意,您通常也需要一個虛擬析構函數,但我正在跳過此為簡單起見):

class Token {
public:
    virtual void somethingSpecial() {
        std::cout << "Hello!" << std::endl;
    }
}

class Literal : public Token {
public:
    virtual void somethingSpecial() {
        std::cout << "I'm a literal!" << std::endl;
    }
}

class Operator : public Token {
public:
    virtual void somethingSpecial() {
        std::cout << "I'm an operator!" << std::endl;
    }
}

然后,在迭代中,您可以執行以下簡單操作:

std::vector<Token*> tokens;

tokens.push_back(new Literal());
tokens.push_back(new Operator());
tokens.push_back(new Literal());

for (std::vector<Token*>:iterator a = tokens.begin(); a != tokens.end(); ++a)
    a->somethingSpecial();

結果將類似於您的代碼。 正在運行的實際代碼將基於子類中的實際實現。 在這種情況下,您最終得到以下輸出:

我是字面意思!

我是運營商!

我是字面意思!

如果一個子類沒有實現虛函數,那么將調用基類的版本(除非你把它作為抽象;然后它將是必須的)。

你可以這樣做:

class CToken {
  public:
    virtual bool isLiteral(void) const = 0;
//...
};

並在兩個子類上定義它。

然后,如果你想使用運算符或文字的事實,解決方案是聲明一個getValue()函數,但絕不使用switch(...) 事實上,如果你想要去認為Obect導向的做法,你應該creeate在虛擬函數CToken這允許子類自動解釋自己,運營商為運營商和值值。

永遠不應該失敗 - 除非你需要。

一個簡單的例子就是在框架周圍傳遞消息。 假設每個消息類型都必須是可序列化的,因此基類接口將提供由被調用的類覆蓋的虛擬“serialise()”方法,並且消息傳遞框架將對所有類型的所有消息進行多態處理。

然而,這些消息可能代表什么,因此它們擁有的屬性和行為可能大不相同 - 從GPS坐標到電子郵件到星歷數據的任何事情,以及嘗試捕獲基類界面中所有多樣性的情況都沒有多大意義。 一旦代理收到消息,它知道它感興趣(根據消息類型)然后(並且只有那時)向下轉換到實際的正確類型才能訪問有意義的消息內容是合適的 - 因為你已經達到了消息不能以純粹抽象的方式處理。

向下傾斜本身並不是一種失敗。 使用RTTI的成本遠遠高於添加一個簡單的枚舉字段,並且經常推薦的“保持動態投射直到某些東西起作用”應該比我想象中發明的更加殘酷的懲罰!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM