2016-12-22 2 views
0

Ich beobachte ein ziemlich seltsames Verhalten. Hier ist minimal Beispiel:Zeiger auf Elemente in einem Vektor sind instabil.

#include <iostream> 
#include <vector> 
#include <stdexcept> 
#include "sparsepp.h" 


enum Version { 
    hg19, 
    hg38 
}; 


enum Base { 
    A, 
    T, 
    G, 
    C 
}; 


typedef struct { 
    Base ref; 
    float a; 
    float c; 
    float g; 
    float t; 
} SNV; 



class GenomeMap { 

private: 
    spp::sparse_hash_map<long, SNV*> * hg19_mapping; 
    spp::sparse_hash_map<long, SNV*> * hg38_mapping; 
    std::vector<SNV> values; 

public: 
    GenomeMap() { 

     hg19_mapping = new spp::sparse_hash_map<long, SNV*>; 
     hg38_mapping = new spp::sparse_hash_map<long, SNV*>; 

    } 

    void add(long hg19pos, long hg38pos, SNV value) { 
     values.push_back(value); 
     (*hg19_mapping)[hg19pos] = &(values.back()); 
     (*hg38_mapping)[hg38pos] = &(values.back()); 
    } 

    float get(Version version, long position, Base ref, Base alt) { 
     spp::sparse_hash_map<long, SNV*> * mapping = (version == hg19) ? hg19_mapping : hg38_mapping; 
     SNV* value = (*mapping)[position]; 

     if (!value || ref != value->ref) 
      return -1; 

     switch (alt) { 
      case A: 
       return value->a; 
      case C: 
       return value->c; 
      case G: 
       return value->g; 
      case T: 
       return value->t; 
     } 
     throw std::invalid_argument("Invalid arguments"); 
    } 

    ~GenomeMap() { 
     delete this->hg19_mapping; 
     delete this->hg38_mapping; 
    } 
}; 


int main(void) { 
    SNV value = {A, 0.1, 0.2, -1.0, 0.3}; 

    GenomeMap mapping; 
    mapping.add(1, 2, value); 
    mapping.add(2, 3, value); 
    std::cout << mapping.get(hg19, 1, A, T) << "\n"; 
    std::cout << mapping.get(hg19, 1, A, T) << "\n"; 
    std::cout << mapping.get(hg19, 2, T, G) << "\n"; 
    std::cout << mapping.get(hg19, 2, A, G) << "\n"; 
    std::cout << mapping.get(hg38, 1, A, T) << "\n"; 
    std::cout << mapping.get(hg38, 1, A, T) << "\n"; 
    std::cout << mapping.get(hg38, 2, T, G) << "\n"; 
    std::cout << mapping.get(hg38, 2, A, G) << "\n"; 

    return 0; 

} 

Der sparsepp.h Header (aus this Repo genommen) definiert die hashmap sparse_hash_map. Wenn ich laufen in diesem Beispiel statt diese Ausgabe

0.3 
0.3 
-1 
-1.1 
-1 
0.3 
-1 
-1.1 

die meiste Zeit erhalte ich:

0.3 
0 
-1 
-1.1 
-1 
-1 
-1 
1.4013e-45 

Einmal in ein, während die zweite Zeile 0.3 ist wie es sein sollte. Ich glaube, da ist etwas schrecklich Dummes, das ich vermisst habe. Ich habe versucht, sparse_hash_map durch std::map von STL zu ersetzen, aber nichts hat sich geändert.

+0

Warum verwenden Sie nicht einen intelligenten Zeiger (z. B. 'std :: unique_ptr') als Wert? –

+0

@ πάνταῥεῖ Ich habe in einer streng C-kompatiblen Teilmenge von C++ für eine lange Zeit codiert, so dass ich nicht weiß, was 'std :: unique_ptr' ist. Ich habe diese Einschränkung nicht mehr. –

+0

Hier gehen Sie: http://en.cppreference.com/w/cpp/memory –

Antwort

4

Ihr Problem ist

void add(long hg19pos, long hg38pos, SNV value) { 
    values.push_back(value);     // <<<<======= HERE 
    (*hg19_mapping)[hg19pos] = &(values.back()); 
    (*hg38_mapping)[hg38pos] = &(values.back()); 
} 

Wenn Sie einen neuen Wert in values schieben Sie alle zuvor zugewiesenen Zeiger ungültig machen. Ihre Optionen sind:

  • Reserve genügend Platz in values, bevor Sie beginnen Hinzufügen Sie, um sicherzustellen, nie den Vektor umverteilen. Dies ist nur möglich, wenn Sie vor dem Start die endgültige Größe kennen.

  • Speichern Indizes in den Vektor, anstelle von Zeigern. Der Index (values.size()-1) wird stabil sein.

+0

Vielen Dank. Das Speichern von Indizes als "insigned long" wäre sogar effizienter als das Speichern von 64-Bit-Zeigern. –

+0

Nur wenn Sie auf 64-Bit-Fenstern sind. Auf allen anderen Plattformen (in der Praxis) sind Länge und Zeiger gleich groß –

Verwandte Themen