shared_ptr a un array: dovrebbe essere usato?


172

Solo una piccola domanda riguardante shared_ptr.

È buona norma utilizzare il shared_ptrpuntamento a un array? Per esempio,

shared_ptr<int> sp(new int[10]);

Se no, allora perché no? Uno dei motivi di cui sono già a conoscenza è che non è possibile aumentare / diminuire il shared_ptr. Quindi non può essere usato come un normale puntatore a un array.


2
FWIT, potresti anche considerare di usare solo std::vector. Dovresti fare attenzione a passare l'array in giro usando i riferimenti in modo da non farne copie. La sintassi per l'accesso ai dati è più pulita di shared_ptr e il ridimensionamento è molto semplice. E ottieni tutta la bontà STL se mai lo desideri.
Nicu Stiurca,

6
Se la dimensione dell'array viene determinata al momento della compilazione, si potrebbe anche considerare l'utilizzo std::array. È quasi uguale a un array non elaborato, ma con una semantica adeguata per l'uso nella maggior parte dei componenti di libreria. Soprattutto oggetti di quel tipo vengono distrutti con delete, non delete[]. Diversamente vector, memorizza i dati direttamente nell'oggetto, in modo da non ottenere alcuna allocazione aggiuntiva.
Celtschk,

Risposte:


268

Con C ++ 17 , shared_ptrpuò essere utilizzato per gestire un array allocato dinamicamente. L' shared_ptrargomento template in questo caso deve essere T[N]o T[]. Quindi puoi scrivere

shared_ptr<int[]> sp(new int[10]);

Da n4659, [util.smartptr.shared.const]

  template<class Y> explicit shared_ptr(Y* p);

Richiede: Y deve essere un tipo completo. L'espressione delete[] p, quando Tè un tipo di array o delete p, quando Tnon è un tipo di array, deve avere un comportamento ben definito e non deve generare eccezioni.
...
Note: Quando Tè un tipo di array, questo costruttore non deve partecipare alla risoluzione del sovraccarico a meno che l'espressione non delete[] psia ben formata e sia o Tsia U[N]ed Y(*)[N]è convertibile in T*, sia Tsia U[]e Y(*)[]sia convertibile in T*. ...

Per supportare ciò, il tipo di membro element_typeè ora definito come

using element_type = remove_extent_t<T>;

È possibile accedere agli elementi dell'array utilizzando operator[]

  element_type& operator[](ptrdiff_t i) const;

Richiede: get() != 0 && i >= 0 . Se Tè U[N], i < N. ...
Note: quando Tnon è un tipo di array, non è specificato se viene dichiarata questa funzione membro. Se viene dichiarato, non è specificato quale sia il suo tipo di ritorno, tranne che la dichiarazione (sebbene non necessariamente la definizione) della funzione deve essere ben formata.


Prima di C ++ 17 , nonshared_ptr poteva essere utilizzato per gestire array allocati dinamicamente. Per impostazione predefinita, chiamerà l'oggetto gestito quando non vi saranno più riferimenti. Tuttavia, quando si assegna l'utilizzo è necessario chiamare e non per liberare la risorsa.shared_ptrdeletenew[]delete[]delete

Per utilizzare correttamente shared_ptrun array, è necessario fornire un deleter personalizzato.

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

Creare shared_ptr come segue:

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

Ora shared_ptrchiamerà correttamente delete[]quando si distrugge l'oggetto gestito.

Il deleter personalizzato sopra può essere sostituito da

  • la std::default_deletespecializzazione parziale per i tipi di array

    std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
  • un'espressione lambda

    std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });

Inoltre, a meno che tu non abbia effettivamente bisogno di condividere la condivisione dell'oggetto gestito, a unique_ptrè più adatto per questa attività, poiché ha una specializzazione parziale per i tipi di array.

std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]

Modifiche introdotte dalle estensioni C ++ per i fondamenti della biblioteca

Un'altra alternativa pre-C ++ 17 a quelle sopra elencate è stata fornita dalla Specifica tecnica sui fondamenti della biblioteca , che è stata ampliata shared_ptrper consentirgli di lavorare fuori dagli schemi per i casi in cui possiede una serie di oggetti. La bozza attuale delle shared_ptrmodifiche previste per questo TS è disponibile in N4082 . Queste modifiche saranno accessibili tramite lo std::experimentalspazio dei nomi e incluse <experimental/memory>nell'intestazione. Alcune delle modifiche rilevanti a supporto shared_ptrdegli array sono:

- La definizione del tipo di membro element_typecambia

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

- Il membro operator[]è stato aggiunto

 element_type& operator[](ptrdiff_t i) const noexcept;

- A differenza della unique_ptrspecializzazione parziale per gli array, entrambi shared_ptr<T[]>e shared_ptr<T[N]>saranno validi ed entrambi risulteranno delete[]chiamati sull'array di oggetti gestito.

 template<class Y> explicit shared_ptr(Y* p);

Richiede : Ydeve essere un tipo completo. L'espressione delete[] p, quando Tè un tipo di array, o delete p, quando Tnon è un tipo di array, deve essere ben formata, deve avere un comportamento ben definito e non deve generare eccezioni. Quando Tè U[N], Y(*)[N]deve essere convertibile in T*; quando Tè U[], Y(*)[]deve essere convertibile in T*; in caso contrario, Y*sarà convertibile in T*.


9
+1, osservazione: c'è anche quello di Boost shared-array.
jogojapan,

5
@ tshah06 shared_ptr::getrestituisce un puntatore all'oggetto gestito. Quindi puoi usarlo comesp.get()[0] = 1; ... sp.get()[9] = 10;
Pretorio

55
ALT: std::shared_ptr<int> sp( new int[10], std::default_delete<int[]>() );vedi anche en.cppreference.com/w/cpp/memory/default_delete
yohjp

2
@Jeremy Se la dimensione è nota al momento della compilazione, non è necessario scrivere una classe per questo, std::shared_ptr<std::array<int,N>>dovrebbe essere sufficiente.
Pretorio

13
Perché unique_ptrottiene quella specializzazione parziale ma shared_ptrnon lo fa?
Adam,

28

Un'alternativa forse più semplice che potresti essere in grado di utilizzare è shared_ptr<vector<int>>.


5
Sì. Oppure un vettore è un superset di un array: ha la stessa rappresentazione in memoria (più metadati) ma è ridimensionabile. Non ci sono davvero situazioni in cui si desidera un array ma non è possibile utilizzare un vettore.
Timmmm,

2
La differenza, qui, è che la dimensione del vettore è più statica e l'accesso ai dati verrà effettuato con una doppia indiretta. Se le prestazioni non sono un problema critico, questo funziona, altrimenti la condivisione di un array potrebbe avere un suo motivo.
Emilio Garavaglia,

4
Quindi probabilmente puoi usare shared_ptr<array<int, 6>>.
Timmmm,

10
L'altra differenza è che è leggermente più grande e più lento di un array grezzo. Generalmente non è un vero problema, ma non facciamo finta che 1 == 1.1.
Andrew,

2
Ci sono situazioni in cui l'origine dei dati nell'array indica che è inutilmente o inutile convertire in un vettore; come quando si ottiene una cornice da una fotocamera. (O, questo è il mio intendimento, comunque)
Narfanator
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.