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.