Domanda

Innanzitutto mi scuso per il lungo periodo che ha preceduto una domanda così semplicistica.

Sto implementando una classe che funge da indice unidimensionale molto lungo su una curva di riempimento dello spazio o dalla tupla n che rappresenta la coordinata cartesiana a cui corrisponde l'indice.

class curvePoint
{
public:
    friend class curveCalculate;

    //Construction and Destruction
    curvePoint(): point(NULL), dimensions(0) {}
    virtual ~curvePoint(){if(point!=NULL) delete[] point;}

    //Mutators
    void convertToIndex(){ if(isTuple()) calc(this); }
    void convertToTuple(){ if(isIndex()) calc(this); }
    void setTuple(quint16 *tuple, int size);
    void setIndex(quint16 *index, int size);
    void setAlgorithm(curveType alg){algorithm = alg;}

    //Inspectors
    bool isIndex(){return current==Index;}
    bool isTuple(){return current==Tuple;}
    size_t size(){return dimensions;}
    quint16 operator[](size_t index);

    enum curveType{HilbertCurve, ZCurve, GrayCodeCurve};
    enum status{Index, Tuple};

private:
    curveCalculate calc;
    curveType algorithm;
    quint16 *point;
    size_t dimensions;
    status current;
};

(La lunghezza dell'array puntato da punto È dimensioni)

Comunque nell'implementazione di operator[] mi chiedevo quale sia il metodo migliore per ottenere il controllo dei limiti.Voglio evitare di generare eccezioni, se possibile, e l'intero intervallo di valori è utilizzabile per ciascun numero nell'array, quindi non è nemmeno possibile un valore speciale da restituire in caso di errore fuori limite;

Stavo pensando a qualcosa del genere anche se implementato nella definizione della classe:

quint16 curvePoint::operator[](size_t index)
{
    return point[ index % dimensions ];
}

Questo fa sì che non usciamo mai dai limiti dell'array e, se ben documentato, penso che andrebbe bene;tuttavia, sono diffidente riguardo a questa particolare implementazione.

Sembra accettabile per gli altri?Esiste un altro modo per eseguire il controllo dei limiti pur soddisfacendo i miei vincoli?

Modificare:Il calcolo di cose come le curve di Hilbert ecc. è estremamente disordinato, abbastanza disordinato da non volere che l'interfaccia aggiuntiva per le librerie STL sia d'intralcio.

Inoltre, poiché dovrò convertirne molte migliaia ogni volta che viene interrogato il database multidimensionale, non voglio il costo aggiuntivo delle chiamate alle funzioni STL nel mix, se possibile.

Mi piace piuttosto l'idea dell'affermazione;ma, se ricordo bene, ci sono interruzioni nelle build di rilascio, non è vero?

Suppongo di poter usare le eccezioni, sembra essere ciò per cui tutti fanno il tifo, ma sto usando le librerie Qt e quelle evitano le eccezioni sia per le prestazioni che per la portabilità e speravo di fare lo stesso.

È stato utile?

Soluzione

La soluzione più semplice è fare come fa C ++ stesso. Ciò limita la quantità di sorprese che i tuoi utenti sperimenteranno.

Il C ++ stesso è abbastanza coerente. Sia il [] incorporato sui puntatori che il std::vector::operator[] hanno un comportamento indefinito se si utilizza un indice di array non associato. Se vuoi controllare i limiti, sii esplicito e usa std::vector::at

Quindi, se fai lo stesso per la tua classe, puoi documentare il comportamento fuori limite come " standard " ;.

Altri suggerimenti

  

Comunque nell'implementazione di   operatore [] Mi chiedevo quale fosse il   metodo migliore per ottenere il controllo dei limiti   è. Voglio evitare di lanciare   eccezioni se possibile, e il   è utilizzabile l'intera gamma di valori   ogni numero nell'array quindi uno speciale   valore da restituire in caso di out of   neanche l'errore dei limiti;

Quindi le restanti opzioni sono:

  • Design flessibile. Cosa hai fatto. & Quot; quot Fissare &; l'input non valido in modo che tenti di fare qualcosa di sensato. Vantaggio: la funzione non si arresta in modo anomalo. Svantaggio: i chiamanti che non hanno idea che accedono a un elemento fuori limite avranno di conseguenza una menzogna . Immagina un edificio di 10 piani con piani da 1 a 10:
  

Tu: " Chi vive al 3 ° piano? "

     

Io: "Mary".

     

Tu: " Chi vive al nono piano? "

     

Io: "Joe".

     

Tu: " Chi vive al 1.203 ° piano? "

     

Io: (aspetta ... 1.203% 10 = 3 ...)   & gt; & Quot; quot Maria &;.

     

Tu: " Wow, Mary deve godere di fantastiche viste da lassù . Quindi possiede due appartamenti allora? & Quot;

  • Un parametro bool output indica l'esito positivo o negativo. Questa opzione di solito finisce con un codice non molto utilizzabile. Molti utenti ignoreranno il codice di ritorno. Ti resta ancora ciò che restituisci nell'altro valore di ritorno.

  • Progetta per contratto. Asserire che il chiamante è entro i limiti. (Per un approccio pratico in C ++, vedere Un'eccezione o un bug? Di Miro Samek o Supporto semplice per la progettazione mediante contratto in C ++ di Pedro Guerreiro .)

  • Restituisce un System.Nullable<quint16> . Oops, aspetta, questo non è C #. Bene, potresti restituire un puntatore a un quint16. Questo ovviamente ha molte implicazioni che non discuterò qui e che probabilmente rendono questa opzione non utilizzabile.

Le mie scelte preferite sono:

  • Per l'interfaccia pubblica di una libreria rilasciata pubblicamente: l'input verrà controllato e verrà generata un'eccezione. Hai escluso questa opzione, quindi non è un'opzione per te. È ancora la mia scelta per l'interfaccia di una libreria rilasciata pubblicamente.
  • Per codice interno: progettazione per contratto.

Per me questa soluzione è inaccettabile perché puoi nascondere un bug molto difficile da trovare. Lanciare un'eccezione fuori portata è la strada da percorrere, o almeno inserire un'asserzione nella funzione.

Se ciò di cui hai bisogno è una sorta di " circolare " matrice di punti, allora la tua soluzione è ok. Tuttavia, per me, sembra proprio nascondere il missaggio dell'operatore di indicizzazione dietro qualche & Quot; sicuro & Quot; logica, quindi sarei contrario alla soluzione proposta.

Se non si desidera consentire l'overflow dell'indice, è possibile verificare e generare un'eccezione.

quint16 curvePoint::operator[](size_t index)
{
    if( index >= dimensions)
    {
       throw std::overflow_error();
    }
    return point[ index ];
}

Se si desidera ridurre il sovraccarico, è possibile evitare le eccezioni utilizzando le asserzioni relative al tempo di debug (si supponga che l'indice fornito sia sempre valido):

quint16 curvePoint::operator[](size_t index)
{
    assert( index < dimensions);
    return point[ index ];
}

Tuttavia, suggerisco che, invece di usare i membri punto e dimensione, utilizzare uno std :: vector < quint16 gt &; per la memorizzazione dei punti. Ha già un accesso basato sull'indice che puoi usare:

quint16 curvePoint::operator[](size_t index)
{
    // points is declared as std::vector< quint16> points;
    return points[ index ];
}

Avere un operatore [] che non fallisce mai suona bene, ma può nascondere bug in seguito, se una funzione chiamante utilizza un offset illegale, trova un valore dall'inizio del buffer e procede come se fosse un valore valido.

Il metodo migliore per ottenere il controllo dei limiti sarebbe quello di aggiungere un'asserzione.

quint16 curvePoint::operator[](size_t index)
{
    assert(index < dimensions);
    return point[index];
}

Se il tuo codice dipende già dalle librerie Boost, potresti invece utilizzare BOOST_ASSERT.

Se fossi in te seguirei l'esempio impostato dalla stl.

In questo caso std::vector fornisce due metodi: at che è controllato i limiti e operator[] che non lo è. Ciò consente al client di decidere con la versione da utilizzare. Non userei sicuramente % size(), poiché questo nasconde solo il bug. Tuttavia, il controllo dei limiti aggiungerà un sacco di sovraccarico per quando si scorre su una grande raccolta, questo è il motivo per cui dovrebbe essere facoltativo. Anche se concordo con altri poster, l'affermazione è un'ottima idea in quanto ciò provocherà solo un aumento delle prestazioni nelle build di debug.

Dovresti anche considerare di restituire riferimenti e fornire versioni const e non const. Ecco le dichiarazioni di funzione per <=> :

reference at(size_type _Pos);
const_reference at(size_type _Pos) const;

reference operator[](size_type _Pos);
const_reference operator[](size_type _Pos) const;

Come buona regola empirica se non sono sicuro di come specificare un'API, cerco esempi di come gli altri specificano API simili. Inoltre, quando utilizzo un'API cerco di giudicare o valutare, trovare i bit che mi piacciono e che non mi piacciono.

Grazie al commento sulla funzione C # nel post di Daniel Daranas sono riuscito a capire una possibile soluzione. Come ho affermato nella mia domanda, sto usando le librerie Qt. Lì per posso usare QVariant. QVariant può essere impostato su uno stato non valido che può essere verificato dalla funzione che lo riceve. Quindi il codice diventerebbe qualcosa del tipo:

QVariant curvePoint::operator[](size_t index){
    QVariant temp;
    if(index > dimensions){
        temp = QVariant(QVariant::Invalid);
    }
    else{
        temp = QVariant(point[index]);
    }

    return temp;
}

Naturalmente questo ha il potenziale di inserire un po 'di sovraccarico gnarly nella funzione, quindi un'altra possibilità è quella di utilizzare un modello di coppia.

std::pair<quint16, bool> curvePoint::operator[](size_t index){
    std::pair<quint16, bool> temp;
    if(index > dimensions){
        temp.second = false;
    }
    else{
        temp.second = true;
        temp.first = point[index];
    }
    return temp;
}

Oppure potrei usare un QPair, che ha esattamente le stesse funzionalità e lo farebbe in modo che l'STL non debba essere collegato.

Potresti forse aggiungere un " fuori dai limiti " eccezione all'operatore [] (o almeno un'affermazione).

Questo dovrebbe rilevare eventuali problemi, specialmente durante il debug.

A meno che non stia drasticamente fraintendendo qualcosa,

return point[ index % dimensions ];

non controlla affatto i limiti. Sta restituendo un valore reale da una parte completamente diversa della linea, il che renderà molto più difficile rilevare i bug.

Vorrei:

  1. Genera un'eccezione o un'asserzione (anche se hai detto che non vuoi farlo)
  2. Semplicemente punto di dereferenza oltre l'array in un " naturale " modo (ovvero salta qualsiasi controllo interno). Il vantaggio rispetto a% è che è più probabile (anche se indefinito non definito) ottenere & Quot; weird & Quot; valori e / o una violazione di accesso

Alla fine, il chiamante sta violando le tue condizioni preliminari e puoi fare qualunque cosa tu voglia. Ma penso che queste siano le opzioni più ragionevoli.

Considera anche cosa C & # 259; t & # 259; lin ha detto sull'incorporazione delle raccolte STL integrate, se è ragionevole.

La tua soluzione sarebbe gradita se fornissi l'accesso ai punti di una forma ellittica. Ma porterà a bug molto cattivi se lo usi per funzioni geometriche arbitrarie, perché fornisci consapevolmente un valore falso.

L'operatore modulo funziona sorprendentemente bene per gli indici di array: implementa anche indici negativi (ad es. point[-3] = point[dimensions - 3]).È facile lavorare con questo, quindi personalmente consiglierei l'operatore modulo purché sia ​​ben documentato.

Un'altra opzione è quella di consentire al chiamante di scegliere la politica fuori limite. Prendere in considerazione:

template <class OutOfBoundsPolicy>
quint16 curvePoint::operator[](size_t index)
{
    index = OutOfBoundsPolicy(index, dimensions);
    return point[index];
}

Quindi è possibile definire diversi criteri che il chiamante può scegliere. Ad esempio:

struct NoBoundsCheck {
    size_t operator()(size_t index, size_t /* max */) {
        return index;
    }
};

struct WrapAroundIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        return index % max;
    }
};

struct AssertIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        assert(index < max);
        return index % max;
    }
};

struct ThrowIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        if (index >= max) throw std::domain_error;
        return index;
    }
};

struct ClampIfOutOfBounds {
    size_t operator()(size_t index, size_t max) {
        if (index >= max) index = max - 1;
        return index;
    }
};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top