Condividi tramite


Contenitori della libreria standard C++

La libreria standard fornisce vari contenitori indipendenti dai tipi per archiviare le raccolte di oggetti correlati. I contenitori sono modelli di classe. Quando si dichiara una variabile contenitore, si specifica il tipo di elementi che il contenitore conterrà. I contenitori possono essere costruiti con un elenco di inizializzatori. Dispongono di funzioni membro per l'aggiunta e la rimozione di elementi e l'esecuzione di altre operazioni.

Per scorrere gli elementi di un contenitore e accedere ai singoli elementi vengono usati gli iteratori. È possibile usare gli iteratori in modo esplicito usando le funzioni membro e gli operatori e le funzioni globali. È possibile inoltre utilizzarli in modo implicito, ad esempio tramite un ciclo range-for. Gli iteratori per tutti i contenitori della libreria standard C++ sono caratterizzati da un'interfaccia comune, ma ogni contenitore definisce i propri iteratori specializzati.

I contenitori possono essere suddivisi in tre categorie: contenitori sequenza, contenitori associativi e adattatori del contenitore.

Contenitori di sequenza

I contenitori sequenziali mantengono l'ordinamento degli elementi inseriti specificati.

Un contenitore vector si comporta come una matrice, ma può automaticamente crescere come richiesto. È ad accesso casuale, viene archiviato in modo contiguo e la lunghezza è estremamente flessibile. Per questi e altri motivi, vector è il contenitore sequenziale preferito per la maggior parte delle applicazioni. In caso di dubbi circa il tipo di contenitore sequenziale da usare, iniziare con un vettore. Per altre informazioni, vedere vector Classe.

Un array contenitore presenta alcuni dei punti di forza di vector, ma la lunghezza non è così flessibile. Per altre informazioni, vedere array Classe.

Un contenitore deque (coda a doppia terminazione) consente inserimenti ed eliminazioni veloci all'inizio e alla fine del contenitore. Condivide i vantaggi di accesso casuale e di lunghezza flessibile di vector, ma non è contiguo. Per altre informazioni, vedere deque Classe.

Un list contenitore è un elenco collegato doubly che consente l'accesso bidirezionale, gli inserimenti rapidi e le eliminazioni rapide in qualsiasi punto del contenitore, ma non è possibile accedere in modo casuale a un elemento nel contenitore. Per altre informazioni, vedere list Classe.

Un contenitore forward_list è un elenco collegato singolarmente, la versione con accesso in avanti di list. Per altre informazioni, vedere forward_list Classe.

Contenitori associativi

Nei contenitori associativi, gli elementi vengono inseriti in un ordine predefinito, ad esempio in ordinamento crescente. Sono inoltre disponibili contenitori associativi non ordinati. I contenitori associativi possono essere raggruppati in due subset: mappe e set.

Una map, talvolta definita dizionario, è costituita da una coppia chiave/valore. La chiave viene utilizzata per ordinare la sequenza e il valore è associato a tale chiave. Ad esempio, una map potrebbe contenere le chiavi che rappresentano ogni parola univoca di un testo e i valori corrispondenti che rappresentano il numero di volte in cui ogni parola viene visualizzata nel testo. La versione non ordinata di map è unordered_map. Per altre informazioni, vedere map Classe e unordered_map classe.

Un set è semplicemente un contenitore crescente di elementi univoci: il valore è anche la chiave. La versione non ordinata di set è unordered_set. Per altre informazioni, vedere set Classe e unordered_set classe.

Sia map che set consentono l'inserimento nel contenitore solo di un'unica istanza di una chiave o di un elemento. Se vengono richieste più istanze di elementi, utilizzare multimap o multiset. Le versioni non ordinate sono unordered_multimap e unordered_multiset. Per altre informazioni, vedere multimap Classe, unordered_multimap Classe, multiset Classe e unordered_multiset Classe.

Le mappe e i set ordinati supportano gli iteratori bidirezionali e le rispettive controparti non ordinate supportano gli iteratori in avanti. Per altre informazioni, vedere Iteratori.

Ricerca eterogenea nei contenitori associativi (C++14)

I contenitori associativi ordinati (mapping, multimap, set e multiset) supportano ora ricerche eterogenee, il che significa che non è più necessario passare lo stesso tipo di oggetto della chiave o dell'elemento nelle funzioni membro, find() ad esempio e lower_bound(). È invece possibile passare qualsiasi tipo per cui viene definito un operator< sottoposto a overload, che consente il confronto con il tipo di chiave.

La ricerca eterogenea viene abilitata in base al consenso esplicito quando si specifica il std::less<> comparatore o std::greater<> "functor a rombo" quando si dichiara la variabile contenitore, come illustrato di seguito:

std::set<BigObject, std::less<>> myNewSet;

Se si usa il criterio di confronto predefinito, il contenitore si comporta esattamente come in C++11 e versioni precedenti.

Nell'esempio seguente viene illustrato come eseguire l'overload operator< per consentire agli utenti di un std::set oggetto di eseguire ricerche semplicemente passando una piccola stringa che può essere confrontata con il membro di BigObject::id ogni oggetto.

#include <set>
#include <string>
#include <iostream>
#include <functional>

using namespace std;

class BigObject
{
public:
    string id;
    explicit BigObject(const string& s) : id(s) {}
    bool operator< (const BigObject& other) const
    {
        return this->id < other.id;
    }

    // Other members....
};

inline bool operator<(const string& otherId, const BigObject& obj)
{
    return otherId < obj.id;
}

inline bool operator<(const BigObject& obj, const string& otherId)
{
    return obj.id < otherId;
}

int main()
{
    // Use C++14 brace-init syntax to invoke BigObject(string).
    // The s suffix invokes string ctor. It is a C++14 user-defined
    // literal defined in <string>
    BigObject b1{ "42F"s };
    BigObject b2{ "52F"s };
    BigObject b3{ "62F"s };
    set<BigObject, less<>> myNewSet; // C++14
    myNewSet.insert(b1);
    myNewSet.insert(b2);
    myNewSet.insert(b3);
    auto it = myNewSet.find(string("62F"));
    if (it != myNewSet.end())
        cout << "myNewSet element = " << it->id << endl;
    else
        cout << "element not found " << endl;

    // Keep console open in debug mode:
    cout << endl << "Press Enter to exit.";
    string s;
    getline(cin, s);
    return 0;
}

//Output: myNewSet element = 62F

Le funzioni membro seguenti in map, multimap, set e multiset sono state sottoposte a overload per supportare ricerche eterogenee:

  1. find

  2. numero

  3. lower_bound

  4. upper_bound

  5. equal_range

Adattatori del contenitore

Gli adattatori del contenitore costituiscono una variante del contenitore sequenza o associativo che limitano l'interfaccia per una maggiore semplicità e chiarezza. Gli adattatori contenitore non supportano gli iteratori.

Un contenitore queue segue la semantica FIFO (first-in-first-out). Il primo elemento inserito nella coda è il primo a essere prelevato, ovvero rimosso dalla coda. Per altre informazioni, vedere queue Classe.

Un contenitore priority_queue è organizzato in modo tale che l'elemento con il valore più alto è sempre il primo della coda. Per altre informazioni, vedere priority_queue Classe.

Un contenitore stack segue la semantica LIFO (last in, first out). L'ultimo elemento inserito nello stack è il primo elemento prelevato. Per altre informazioni, vedere stack Classe.

Poiché gli adattatori contenitore non supportano gli iteratori, non possono essere usati con gli algoritmi della libreria standard C++. Per altre informazioni, vedere Algoritmi.

Requisiti per gli elementi contenitore

In genere, gli elementi inseriti in un contenitore della libreria standard C++, se sono copiabili, possono essere di quasi tutti i tipi di oggetto. Gli elementi solo mobili, quali ad esempio vector<unique_ptr<T>> creati mediante unique_ptr<> funzioneranno purché non vengano chiamate le funzioni membro che tentano di copiarli.

Il distruttore non è autorizzato a generare un'eccezione.

I contenitori associativi ordinati, descritti precedentemente in questo articolo, devono disporre di un operatore di confronto pubblico definito. Per impostazione predefinita, l'operatore è operator<, ma anche i tipi che non funzionano con operator< sono supportati.

Alcune operazioni sui contenitori potrebbero anche richiedere un costruttore predefinito pubblico e un operatore di equivalenza pubblico. Ad esempio, i contenitori associativi non ordinati richiedono il supporto per le operazioni di uguaglianza e hashing.

Accesso agli elementi contenitore

Agli elementi dei contenitori si accede tramite gli iteratori. Per altre informazioni, vedere Iteratori.

Nota

È inoltre possibile usare i cicli for basati sull'intervallo per scorrere le raccolte STL.

Confronto tra contenitori

Tutti i contenitori sottopongono a overload l'operatore == per confrontare due contenitori dello stesso tipo che hanno lo stesso tipo di elemento. È possibile usare == per confrontare una stringa vettoriale con un'altra stringa> vettoriale<<, ma non è possibile usarla per confrontare una stringa> vettoriale<con una stringa di elenco<o una stringa>> vettoriale con un vettore<<char*>.> In C++98/03 è possibile usare std::equal o std::mismatch confrontare i tipi di contenitore e/o i tipi di elemento diversi. In C++11 è anche possibile usare std::is_permutation. Ma in tutti questi casi le funzioni presuppongono che i contenitori siano la stessa lunghezza. Se il secondo intervallo è più breve del primo, ne risulta un comportamento indefinito. Se il secondo intervallo è più lungo, i risultati possono ancora risultare errati perché il confronto non va oltre la fine del primo intervallo.

Confronto tra contenitori dissimili (C++14)

In C++14 e versioni successive è possibile confrontare contenitori diversi e/o tipi di elementi diversi usando uno degli overload delle std::equalfunzioni , std::mismatcho std::is_permutation che accettano due intervalli completi. Questi overload consentono di confrontare i contenitori con lunghezze diverse. Questi overload sono molto meno soggetti a errori e ottimizzati in modo da restituire false in un tempo costante quando vengono confrontati i contenitori di lunghezze diverse. Pertanto, è consigliabile usare questi overload, a meno che non si abbia un motivo chiaro da non usare o si stia usando un std::list contenitore, che non trae vantaggio dalle ottimizzazioni a doppio intervallo.

Vedi anche

Contenitori paralleli
<sample container>
Thread Safety in the C++ Standard Library (Sicurezza dei thread nella libreria standard C++)