Składnia
#include <memory>
namespace std
{
template < class T, class...Args >
unique_ptr < T > make_unique( Args &&...args );
template < class T >
std::unique_ptr < T[] > make_unique( std::size_t size );
template < class T, class...Args >
make_unique( Args &&...args ) = delete;
}
Parametry szablonu
Argumenty
Zwracana wartość
Zależnie od wersji użytej funkcji dla typu T:
std::unique_ptr < T >
lub
std::unique_ptr < T[] >
.
Opis szczegółowy
Funkcja zwraca inteligentny wskaźnik
std::unique_ptr < T >
przekazując podane argumenty do konstruktora typu T. Możemy zaalokować pojedyńczy obiekt typu T, lub dynamiczną tablicę obiektów typu T.
Wg
Scotta Meyersa z książki "Skuteczny nowoczesny C++" funkcja
std::make_unique()
powinna być stosowana zamiast ręcznego tworzenia
std::unique_ptr <>
i allokacji za pomocą
operator new
z dwóch powodów:
1. Krótszego zapisu, mniejszej podatności na błędy modyfikacji kodu, mniejszej duplikacji kodu:
std::unique_ptr < std::string > text( new std::string );
auto text = std::make_unique < std::string >();
2. Większego bezpieczeństwa wątkowego.
Mając funkcję np.:
void saveText( std::unique_ptr < std::string > text, int textDepth );
i wywułując np.:
saveText( new std::string( "Ala ma świnkę morską" ), computeDepthLevelFromEnvironment() );
oraz wiedząc, że nie jest zdefiniowana kolejność ewaluacji argumentów funkcji może się zdarzyć sytuacja, że wpierw zaalokujemy obiekt, następnie funkcja computeDepthLevelFromEnvironment() wyrzuci wyjątek -wtedy nastąpi wyciek pamięci. W podobnej sytuacji stosując
std::make_unique
destruktor obiektu
std::unique_ptr
posprząta zaalokowaną pamięć.
Dodatkowe informacje
W razie konieczności podania własnego deletera funkcja
std::make_unique()
tego nie obsługuje, dlatego wtedy musimy utworzyć inteligentny wskaźnik bez użycia funkcji, przykładowo:
#include <iostream>
#include <string>
#include <memory>
int main()
{
auto printingStringDeleter =[]( std::string * text )
{
std::cout << "Deleting: '" << * text << "'\n";
delete text;
};
std::unique_ptr < std::string, decltype( printingStringDeleter ) > dummyText( new std::string( "Ala ma kota" ), printingStringDeleter );
}
Standardowe wyjście programu:
Deleting: 'Ala ma kota'
Kolejną sytuacją kiedy funkcja sprawia problemy jest używanie inicjatorów klamrowych (inicjatory klamrowe nie mogą być przekazywane doskonale):
#include <iostream>
#include <vector>
#include <memory>
#include <iterator>
#include <algorithm>
int main()
{
auto valuesToInitialise = { 1, 3, 5 };
auto vectorPtr = std::make_unique < std::vector < int >>( valuesToInitialise );
std::cout << "\nvector.size() = " << vectorPtr->size() << ", elements: ";
std::copy( vectorPtr->cbegin(), vectorPtr->cend(), std::ostream_iterator < int >( std::cout, ", " ) );
std::cout << std::endl;
}
Standardowe wyjście programu:
vector.size() = 3, elements: 1, 3, 5,
Rzucane wyjątki
Funkcja może wyrzucić te same wyjątki, które może wyrzucić konstruktor obiektu typu T. Może również być wyrzucony
std::bad_alloc
w razie braku pamięci.
Wymagania
Kompilator zgodny przynajmniej z C++14 powinien dostarczyć funkcję
std::make_unique()
.
Jeśli nasz kompilator jej nie posiada, a posiada
std::unique_ptr <>
możemy użyć przykładowej implementacji z tego artykułu. W ostateczności możemy użyć inteligętnego wskaźnika z biblioteki boost:
boost::make_unique.
Implementacja
Zaczerpnięta z
En.cppreference.com:
template < typename T, typename...Arg >
std::unique_ptr < T > make_unique( Arg &&...args )
{
return std::unique_ptr < T >( new T( std::forward < Arg >( args )...) );
}
Przykład
#include <iostream>
#include <vector>
#include <memory>
#include <iterator>
#include <algorithm>
int main()
{
auto vectorPtr = std::make_unique < std::vector < int >>();
std::cout << "vector.size() = " << vectorPtr->size() << ", elements: ";
std::copy( vectorPtr->cbegin(), vectorPtr->cend(), std::ostream_iterator < int >( std::cout, ", " ) );
vectorPtr = std::make_unique < std::vector < int >>( 2, 3 );
std::cout << "\nvector.size() = " << vectorPtr->size() << ", elements: ";
std::copy( vectorPtr->cbegin(), vectorPtr->cend(), std::ostream_iterator < int >( std::cout, ", " ) );
std::cout << std::endl;
}
Standardowe wyjście programu:
vector.size() = 0, elements:
vector.size() = 2, elements: 3, 3,
Zagadnienia powiązane
auto_ptr | Przechowuje wskaźnik na zaalokowany obiekt zapewniając jednocześnie jego zwolnienie z chwilą wywołania destruktora. (szablon klasy) |
---|
shared_ptr | Przechowuje wskaźnik i kontroluje liczbę istniejących kopii wskaźnika (automatycznie zwalnia pamięć). (szablon klasy) |
---|
scoped_ptr | Automatycznie zwalnia pamięć zaalokowaną przy pomocy operatora new przy wyjściu ze scope-a. (szablon klasy) |
---|
Linki zewnętrzne
Linki zewnętrzne