2014-07-11 52 views
13

Temel olarak, alan adı (dize) Eşleme türünü Değeriyle eşleyen bir Hashmap içeren MyClass'ı istiyorum. Bu amaçla, & türünü tutan ayrı bir MyField sınıfı yazdım. ...C++ std :: harita tutan her çeşit değer

template <typename T> 
class MyField { 
    T m_Value; 
    int m_Size; 
} 


struct MyClass { 
    std::map<string, MyField> fields; //ERROR!!! 
} 

Ama gördüğünüz gibi ben MyField için tür parametresi sağlamadığı için, harita beyan başarısız

: değer bilgisi ..

Bu

Bugüne kadar ne var

Yani sanırım bir şey olmalı gibi

std::map< string, MyField<int> > fields; 

veya


Ama ilan haritası sadece tutabilir Ben bir harita istiyorum .. belirli bir türdeki myField tutabilir çünkü belli ki bu, benim bütün amacı zayıflatmaktadır

std::map< string, MyField<double> > fields; 

Herhangi bir şekilde MyField clas ..

Bunu başarabilmemin herhangi bir yolu var mı ..?

+4

Bir çeşit silmeye ihtiyacınız var. Ben 'herhangi bir destek' tavsiye ederim. – chris

+0

std :: map > 'işlevlerini kullanabilirsiniz. –

+0

@sharth Sade (void *) yerine shared_ptr 'u kullanmanın bir nedeni var mı? – user3794186

cevap

16

Blindy cevabı (1) çok iyidir, ama sadece cevabı tamamlamak için:

class MyFieldInterface 
{ 
    int m_Size; // of course use appropriate access level in the real code... 
    ~MyFieldInterface() = default; 
} 

template <typename T> 
class MyField : public MyFieldInterface { 
    T m_Value; 
} 


struct MyClass { 
    std::map<string, MyFieldInterface* > fields; 
} 

Artıları:

dinamik devralma kullanarak, hiçbir kütüphane ile bunu yapmak için başka bir yol yoktur
  • herhangi C'ye tanıdık ++ kodlayıcı
  • o (bazı bağlamlarda sen izin verilmez) Boost'u kullanmak için zorlamayın
  • ;

Dezavantajları: bunları işlemek için semantik değerde semantik yerine referans yığın/serbest deposunda nesneleri ayırmak ve kullanmak zorunda

  • ;
  • Bu şekilde ortaya konan kamu mirası, dinamik mirasın aşırı kullanımına ve türlerinizle ilgili çok uzun vadeli sorunların çok fazla birbirine bağlı olmasına neden olabilir; kendi nesnelere sahipse, imha edilmesini yönetmek zorunda olduğunuzdan, bir işaretçi vektörü sorunludur;

Bu nedenle, varsa, boost :: any veya boost :: variantını varsayılan olarak kullanın ve bu seçeneği yalnızca başka şekilde ele alın. Ancak yine de potansiyel olarak daha sorunlu bir nokta vardır

struct MyClass { 
    std::map<string, std::unique_ptr<MyFieldInterface> > fields; // or shared_ptr<> if you are sharing ownership 
} 

:

son eksileri akıllı işaretçileri kullanabilirsiniz işaret düzeltmek için

O Silmek/yeni kullanarak nesneleri oluşturmak için zorlar (veya make_unique/paylaşılan). Bu, gerçek nesnelerin, serbest depoda (yığın), ayırıcı tarafından sağlanan herhangi bir konumda (çoğunlukla varsayılan olan) oluşturulduğu anlamına gelir. Bu nedenle, nesnelerin listesi sık sık cache misses nedeniyle olabildiğince hızlı değil.

diagram of vector of polymorphic objects sen (aşağıdaki değilse görmezden) çok sık mümkün olduğunca hızlı olarak bu listeyi döngü performansı konusunda endişeleriniz varsa

, o zaman daha iyi kullanmak istiyorum ya artırmak :: varyantı (eğer varsa Kullanacağınız tüm beton tiplerini zaten biliyorsunuz) VEYA bir çeşit tip silinen polimorfik kap kullanın.

diagram of polymorphic container

fikri konteyner aynı türden nesnelerin dizilerini yönetmek, ama bu hala aynı arabirim olmasıdır. Bu arabirim ya bir kavram (örümcek yazım teknikleri kullanılarak) veya dinamik bir arabirim (ilk örneğimde olduğu gibi bir temel sınıf) olabilir. Avantajı, konteynerin aynı tip nesneleri ayrı vektörlerde tutacağından, bunların içinden geçmek hızlıdır. Sadece bir türden diğerine gitmek değildir. Nesnelerin takılı sırayı tutmak gerekiyorsa http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html

Ancak, bu teknik faiz kaybetmektedirler: Burada

bir örnek (görüntüler oradan vardır) 'dir.

Herhangi bir şekilde, ihtiyaçlarınızı karşılayan çok sayıda çözüm vardır. Davanızla ilgili yeterli tecrübeye sahip değilseniz, ya benim örneğimde açıkladığım basit çözümü ya da herhangi bir destek/varyantı kullanmanızı öneririm.

15

boost::variant'u (depolayabileceğiniz türleri biliyorsanız derleme zamanı desteği sağlar) veya boost::any'u (gerçekten her tür için) kullanın; ancak bu durum böyle görünmemektedir).

http://www.boost.org/doc/libs/1_55_0/doc/html/variant/misc.html#variant.versus-any

Düzenleme: kendi çözümünüzü haddeleme serin görünebilir, ancak tam, doğru şekilde uygulanmasını kullanılarak uzun vadede baş ağrısı bir çok kazandıracak bu yeterli vurgulamak değildir. boost::any, hem güvenli (typeid()) hem de güvenli olmayan (aptal dökümler) değer alımları olan RHS kopya kurucularını (C++ 11), const corectness, RHS işlenenlerini ve her iki işaretçi ve değer türünü uygular.

Genel olarak bu doğrudur, ancak daha düşük seviyeli taban türleri için uygulamanızın tamamını oluşturursunuz.

5
class AnyBase 
{ 
public: 
    virtual ~AnyBase() = 0; 
}; 
inline AnyBase::~AnyBase() {} 

template<class T> 
class Any : public AnyBase 
{ 
public: 
    typedef T Type; 
    explicit Any(const Type& data) : data(data) {} 
    Any() {} 
    Type data; 
}; 

std::map<std::string, std::unique_ptr<AnyBase>> anymap; 
anymap["number"].reset(new Any<int>(5)); 
anymap["text"].reset(new Any<std::string>("5")); 

// throws std::bad_cast if not really Any<int> 
int value = dynamic_cast<Any<int>&>(*anymap["number"]).data; 
+0

Değeri almak ve fazladan atlamaktan kaçınmak için temel sınıfa bir üye ekleyebilirsiniz ('(Herhangi bir &) (* anymap [" number "]) data' korkunç görünüyor). Ama bu noktada, temel bir destek olan “any” u uyguladınız, tam ve iyi test edilmiş bir sınıfı da kullanabilirsiniz. +1 olsa da, yaratılış nokta! – Blindy

+0

Kesinlikle bunu deneyeceğim! AnyBase'in iki yıkıcısı hakkında bana açıklayabilir misin? Niçin iki tane neden olduğunu ve neden onlara ihtiyacınız olduğunu anlamıyorum .. teşekkürler – user3794186

+0

@ user3794186, Sadece bir desctructor var ve 'virtual' var, çünkü aksi halde türetilenler 'desctructor'lar (ve böylece " Verilerin yok edicileri denemezdi. – Blindy

0

Ayrıca, * bir boşluğu kullanmak ve reinterpret_cast kullanarak doğru türe geri değerini düşürebilir. Bu, geri çağırmalarda sıklıkla kullanılan bir tekniktir.

#include <iostream> 
#include <unordered_map> 
#include <string> 
#include <cstdint> // Needed for intptr_t 
using namespace std; 


enum TypeID { 
    TYPE_INT, 
    TYPE_CHAR_PTR, 
    TYPE_MYFIELD 
};  

struct MyField { 
    int typeId; 
    void * data; 
}; 

int main() { 

    std::unordered_map<std::string, MyField> map; 

    MyField anInt = {TYPE_INT, reinterpret_cast<void*>(42) }; 

    char cstr[] = "Jolly good"; 
    MyField aCString = { TYPE_CHAR_PTR, cstr }; 

    MyField aStruct = { TYPE_MYFIELD, &anInt }; 

    map.emplace("Int", anInt); 
    map.emplace("C String", aCString); 
    map.emplace("MyField" , aStruct ); 

    int   intval = static_cast<int>(reinterpret_cast<intptr_t>(map["Int"].data)); 
    const char *cstr2 = reinterpret_cast<const char *>(map["C String"].data); 
    MyField* myStruct = reinterpret_cast<MyField*>(map["MyField"].data); 

    cout << intval << '\n' 
     << cstr << '\n' 
     << myStruct->typeId << ": " << static_cast<int>(reinterpret_cast<intptr_t>(myStruct->data)) << endl; 
} 
+2

s/'reinterpret_cast' /' static_cast' / – chris

1

C++ 17 birlik çok daha iyi farklı tutma imkanı vardır bir std::variant türü vardır.

C++ 17'de olmayanlar için, boost::variant bu aynı mekanizmayı uygular.

Takviye kullanmayanlar için, https://github.com/mapbox/variant, çok umut verici, iyi belgelenmiş, hafif ve çok kullanımlı örneklere sahip C++ 11 ve C++ 14 için variant'un daha hafif bir sürümünü uygular.

İlgili konular