2017-07-17 85 views
1

我們知道,派生類成員函數可以通過C++中的基類指針訪問,提供的這些成員函數必須是虛擬的。是否有辦法從基類指針訪問派生類成員函數,這些函數不是虛擬的或純虛擬的從基類指針調用派生類非虛擬成員函數

即我想通過基類指針調用僅在派生類&中存在的派生類成員函數。我將如何實現這一目標?

例如,如果我設計一個工廠設計模式,

class Vehicle { 
public: 
    virtual void printVehicle() = 0; 
    static Vehicle* Create(VehicleType type); 
}; 
class TwoWheeler : public Vehicle { 
public: 
    void printVehicle() { 
     cout << "I am two wheeler" << endl; 
    } 
    void Some2WheelerONLYSpecificOPeration() 
    { 

    } 
}; 
class ThreeWheeler : public Vehicle { 
public: 
    void printVehicle() { 
     cout << "I am three wheeler" << endl; 
    } 
void Some3WheelerONLYSpecificOPeration() 
    { 

    } 
}; 
class FourWheeler : public Vehicle { 
    public: 
    void printVehicle() { 
     cout << "I am four wheeler" << endl; 
    } 
void Some4WheelerONLYSpecificOPeration() 
    { 

    } 
}; 

// Factory method to create objects of different types. 
// Change is required only in this function to create a new object type 
Vehicle* Vehicle::Create(VehicleType type) { 
    if (type == VT_TwoWheeler) 
     return new TwoWheeler(); 
    else if (type == VT_ThreeWheeler) 
     return new ThreeWheeler(); 
    else if (type == VT_FourWheeler) 
     return new FourWheeler(); 
    else return NULL; 
} 

int main() 
{ 
Vehicle* basePtr = Vehicle::Create(VT_TwoWheeler); 
basePtr->Some2WheelerONLYSpecificOPeration(); //HOW TO ACHIEVE THIS CALL 

basePtr = Vehicle::Create(VT_ThreeWheeler); 
basePtr->Some3WheelerONLYSpecificOPeration(); //HOW TO ACHIEVE THIS CALL 

basePtr = Vehicle::Create(VT_FourWheeler); 
basePtr->Some4WheelerONLYSpecificOPeration(); // //HOW TO ACHIEVE THIS CALL 
} 
+6

'dynamic_cast'或visitor pattern。 – Jarod42

+3

如果它們不在基類中,那麼你必須明確地轉換到派生類。 –

+0

查看[Template Method](https://sourcemaking.com/design_patterns/template_method)模式。只定義_present,請問您的用例是什麼? – user0042

回答

1

我想調用派生類的成員函數,其僅存在於通過基類派生指針類&不在基類。我將如何實現這一目標?

您不能使用指向基類的指針調用派生類的非虛成員函數。

您需要一個指向派生類的指針。最簡單的方法是使用dynamic_cast獲取指向派生類的指針,檢查轉換是否成功,然後使用派生類指針調用派生類成員函數。

更好的方法是在基類中提供一個虛擬成員函數並在派生類中實現它。

+0

你可以舉個例子,我怎麼在這裏使用dynamic_cast? – codeLover

+2

@codeLover這很可能不是你想要做的。你能詳細說明你想達到什麼嗎?如果只是爲了避免在基類中提供定義,而是使用純虛函數。 'dynamic_cast'的使用非常簡單:http://en.cppreference.com/w/cpp/language/dynamic_cast – user0042

1

你可以用dynamic_cast做你想做的,但是這會導致代碼審查結果令人失望。取而代之的是,我能投你去,你也做了同樣的路線與printVehicle

class Vehicle 
{ 
public: 
    // without a virtual destructor you are walking into 
    // a very bad bug. The wrong destructor may be called. 
    virtual ~Vehicle() 
    { 
    } 
    virtual void printVehicle() = 0; 

    // Specific stuff that all children must provide 
    virtual void doTypeSpecificStuff() = 0; 

    // this is actually a bit of a ideological weird. I'm not sure I can call 
    // it a flaw. By making this factory function a member of Vehicle, Vehicle 
    // must now know its children. If this is the case, the VehicleType enum 
    // should probably be a member of Vehicle, but personally I think this 
    // factory should be a totally free function. 
    static Vehicle* Create(VehicleType type); 
}; 
class TwoWheeler: public Vehicle 
{ 
public: 
    void printVehicle() 
    { 
     cout << "I am two wheeler" << endl; 
    } 
    void doTypeSpecificStuff() 
    { 
     cout << "Doing two wheeler stuff" << endl; 
    } 
}; 

離開的其他兩個類和Vehicle::Create以節省空間。

int main() 
{ 
    Vehicle* basePtr = Vehicle::Create(VT_TwoWheeler); 
    basePtr->doTypeSpecificStuff(); //HOW TO ACHIEVE THIS CALL 
    // leaking memory here, so 
    delete basePtr; 
    // but also look into std::unique_ptr. Much better suited to this behaviour 


} 

事實上,我們約std::unique_ptr現在作用於對最後的評論。 A unique_ptr爲您管理您的動態分配,因此您不必使用delete s來混淆您的代碼,並可能很快就會丟失一個或delete。只要unique_ptr在範圍內,unique_ptr的指針就是有效的。如果你可以編譯,指針是好的,除非你做了一些愚蠢的事情,比如從不指向任何東西或手動刪除指針。

雖然我們在這裏,讓我們消除我早先關於vehicle::Create的擔憂。

首先我們定義一個自由函數來代替Create並返回一個unique_ptr。由於我討厭必須通過我的代碼檢查所有代碼以確保實際創建了一個對象,因此當我們通過拋出異常無法將提供的車輛類型與類匹配時,我們也會對此產生很大的不滿。

而不是一個if - else if s鏈,我們將使用一個更優雅的switch語句。

std::unique_ptr<Vehicle> SmarterVehicleFactory(VehicleType type) 
{ 
    switch (type) 
    { 
     case VT_TwoWheeler: 
      return std::make_unique<TwoWheeler>(); 
     case VT_ThreeWheeler: 
      return std::make_unique<ThreeWheeler>(); 
     case VT_FourWheeler: 
      return std::make_unique<FourWheeler>(); 
     default: 
      throw std::runtime_error("Invalid Vehicle type"); 
    } 
}  

然後我們將使用這個新功能

int main() 
{ 
    try 
    { 
     std::unique_ptr<Vehicle> basePtr = SmarterVehicleFactory(VT_TwoWheeler); 
     basePtr->doTypeSpecificStuff(); 

     basePtr = SmarterVehicleFactory(VT_ThreeWheeler); 
     // unique_ptr freed the TwoWheeler for us. 
     basePtr->doTypeSpecificStuff(); 

     basePtr = SmarterVehicleFactory(VT_FourWheeler); 
     basePtr->doTypeSpecificStuff(); 

     // just for laughs we will ask for a FiveWheeler, which we have not yet 
     // fully implemented 
     basePtr = SmarterVehicleFactory(VT_FiveWheeler); // will throw exception 
     basePtr->doTypeSpecificStuff(); // will not be executed 
    } 
    catch (const std::exception & exc) 
    { 
     cerr << "Rats! Something bad happened: " << exc.what(); 
     // basePtr will be unmodified and still pointing to a FourWheeler 
    } 
} // basePtr will go out of scope here and clean up our memory for us. 

這種方法的好處是不用上課知道任何其他類東西。您可以將Vehicle放入包含SmarterVehicleFactory原型和車輛類型列表的標題中,並隱藏其他所有內容。用戶什麼都看不到。每個人都被矇在鼓裏。

爲什麼這麼好?因爲現在您可以更改上述任何類,但接口類除外,不會影響任何其他類。這使您的代碼更易於維護和調試。

相關問題