簡體   English   中英

C ++中指針的自動返回類型

[英]Automatic Return Type for Pointers in C++

我希望標題不要太混亂。 我所擁有的是一個類StorageManager包含從Storage派生的類的對象列表。 這是一個例子。

struct Storage {};                         // abstract

class StorageManager
{
private:
    map<string, unique_ptr<Storage>> List; // store all types of storage

public:
    template <typename T>
    void Add(string Name)                  // add new storage with name
    {
        List.insert(make_pair(Name, unique_ptr<Storage>(new T())));
    }

    Storage* Get(string Name)              // get storage by name
    {
        return List[Name].get();
    }
};

Say Position是一種特殊的存儲類型。

struct Position : public Storage
{
    int X;
    int Y;
};

感謝上一個問題的重要答案, Add功能已經有效。 我想改進的是Get功能。 它合理地返回一個指針Storage*我可以使用如下所示。

int main()
{
    StorageManager Manager;
    Manager.Add<Position>("pos");    // add a new storage of type position

    auto Strge = Manager.Get("pos"); // get pointer to base class storage
    auto Pstn = (Position*)Strge;    // convert pointer to derived class position

    Pstn->X = 5;
    Pstn->Y = 42;
}

有沒有辦法通過自動返回指向派生類的指針擺脫這個指針轉換? 也許使用模板?

使用:

template< class T >
T* Get(std::string const& name)
{
    auto i = List.find(name);
    return i == List.end() ? nullptr : static_cast<T*>(i->second.get());
}

然后在你的代碼中:

Position* p = Manager.Get<Position>("pos");

除了@BigBoss已經指出的內容之外,我沒有看到你可以為你的Get成員函數做些什么,但你可以改進你的Add成員來返回用過的存儲。

template <typename T>
T* Add(string Name)                  // add new storage with name
{
   T* t = new T();
   List.insert(make_pair(Name, unique_ptr<Storage>(t)));
   return t;
}

// create the pointer directly in a unique_ptr
template <typename T>
T* Add(string Name)                  // add new storage with name
{
  std::unique_ptr<T> x{new T{}};
  T* t = x.get();
  List.insert(make_pair(Name, std::move(x)));
  return t;
}

編輯臨時阻止我們進行dynamic_cast EDIT2實施MatthieuM的建議。

您還可以通過使用默認參數接受要插入的類型的值來進一步改進該功能,但這可能會產生額外的副本。

除了這看起來像一個可怕的想法...讓我們看看我們可以做些什么來改善這種情況。

=>要求默認構造是個壞主意

template <typename T>
T& add(std::string const& name, std::unique_ptr<T> element) {
    T& t = *element;
    auto result = map.insert(std::make_pair(name, std::move(element)));
    if (result.second == false) {
        // FIXME: somehow add the name here, for easier diagnosis
        throw std::runtime_error("Duplicate element");
    }
    return t;
}

=>盲目貶低是一個壞主意

template <typename T>
T* get(std::string const& name) const {
    auto it = map.find(name);
    return it != map.end() ? dynamic_cast<T*>(it->second.get()) : nullptr;
}

但坦率地說,這個系統充滿了漏洞。 首先可能是不必要的。 我鼓勵您回顧一般問題,提出更好的設計。

當你有一個指針或對某個類的對象的引用時,你所知道的是它引用的實際運行時對象是該類或某個派生類 auto在編譯時無法知道對象的運行時類型,因為包含auto變量的代碼段可能位於運行兩次的函數中 - 一次處理一個運行時類型的對象,另一個處理具有不同運行時的對象類型! 類型系統無法告訴您具有多態性的語言中正在使用的確切類型 - 它只能提供一些約束。

如果您知道對象的運行時類型是某個特定的派生類(如您的示例所示),則可以(並且必須)使用強制轉換。 (最好使用static_cast<Position*>形式的強制轉換,因為強制轉換是危險的,這樣可以更容易地在代碼中搜索強制轉換。)

但總的來說,這樣做很多都是設計不佳的標志。 聲明基類並從中派生其他類類型的目的是使所有這些類型的對象能夠以相同的方式處理,而不會轉換為特定類型。

  • 如果您希望在編譯時始終擁有正確的派生類型而不使用強制轉換,那么您別無選擇,只能使用該類型的單獨集合。 在這種情況下,從Storage獲取Position可能沒有意義。
  • 如果您可以重新排列事物,以便StorageManager::Get()的調用者需要處理Position都可以通過調用未指定Position specific特定信息(例如坐標)的函數來完成,那么您可以這些功能在Storage虛函數中,並在Position實現它們的Position specific特定版本。 例如,您可以創建一個函數Storage::Dump() ,它將其對象寫入stdout Position::Dump()將輸出XY ,而其他可想到的派生類的Dump()實現將輸出不同的信息。
  • 有時您需要能夠使用可能是幾種基本上不相關的類型之一的對象。 我懷疑這可能就是這種情況。 在這種情況下, boost::variant<>是一個很好的方法。 該庫提供了一種稱為訪問者模式的強大機制,它允許您指定對variant對象可能采用的每種類型應采取的操作。

暫無
暫無

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

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