2013-08-27 23 views
6

Jeśli nie potrzebujesz dynamicznego wzrostu i nie znasz rozmiaru bufora w czasie kompilacji, kiedy powinno się użyć unique_ptr<int[]> zamiast vector<int>, jeśli w ogóle?unique_ptr <int[]> lub wektor <int>?

Czy występuje znacząca utrata wydajności przy korzystaniu z vector zamiast z unique_ptr?

+10

Dlaczego nie użyć std :: array? – Bart

+5

@Bart Nie musi powiększać pamięci po przydzieleniu, co nie znaczy, że zna rozmiar w czasie kompilacji. – Praetorian

+0

@Bart Zaktualizowałem pytanie, zakładając, że rozmiar bufora nie jest znany podczas kompilacji. – huitlarc

Odpowiedz

4

Nie ma utraty wydajności w korzystaniu std::vector Vs. std::unique_ptr<int[]>. Alternatywy nie są jednak dokładnie równoważne, ponieważ wektor może być hodowany, a wskaźnik nie może (to może być i zaletą lub wadą, czy wektor rośnie przez pomyłkę?)

Istnieją inne różnice, takie jak fakt, że wartości zostaną zainicjowane w std::vector, ale nie będą one, jeśli new tablicy (chyba że używasz wartość-inicjalizacji ...).

Pod koniec dnia osobiście wybrałbym std::vector<>, ale nadal koduję w C++ 03 bez std::unique_ptr.

+0

Ponadto, nie zapominajmy, że nie ma informacji o rozmiarze za pomocą metody 'unique_ptr'. –

4

C++ 14 wprowadza std :: dynarray w tym celu.

Teraz między tymi dwoma konstrukcjami:

  1. auto buffer = std::make_unique<int[]>(someCount);
  2. auto buffer = std::vector<int>(someCount, someValue);

Pierwszy daje niezainicjowanej tablicę int ale drugi inicjuje go o wartości (0 jeśli nie zapewniają). Więc jeśli nie potrzebujesz pamięci, aby zostać zainicjowane, ponieważ będzie go zastąpić jakoś później z czymś bardziej skomplikowanym niż std::fill, wybierz 1, jeśli nie, wybierz 2.

+6

C++, czym się stałeś ... – GManNickG

+4

Myślałem, że 'dynarray' został wprowadzony do trollowania programistów C++. –

+0

@Mystical Historia "dynarray" jest bardziej subtelna niż podstawowy troll. Wymaga nie tyle umiejętności, aby napisać podstawowy "dynarray", jak o stałym rozmiarze "wektor" ze standardową alokacją dynamiczną. Ale prawdziwa tajemnica tego obiektu polega na umożliwieniu kompilatorowi decydowania, gdzie przydzielić pamięć, może być na stercie, ale może być na stosie. A to jest coś niemożliwego do zrobienia po prostu nawet z C++ 11. – galop1n

3

std::vector przechowuje długość zarówno wielkości zmiennej, jak i wielkości przydzielonych danych wraz ze wskaźnikiem do danych, które są samodzielne. std::unique_ptr po prostu przechowuje wskaźnik, więc może być niewielki wzrost w użyciu std::unique_ptr.

Nikt jeszcze nie wspomniał o tym, że wektor zawiera iteratory i funkcję taką i size(), gdzie unikalny ptr nie. Więc jeśli iteratory są potrzebne Użyj std::vector

1

Cel Part:

Nie, chyba nie powinna być znacząca różnica wydajności między nimi (choć przypuszczam, że to zależy od wykonania i należy mierzyć jeśli jest krytyczna) .

Subiektywna części:

std::vector ma zamiar dać Ci dobrze znany interfejs z .size() i .at() i iteratorów, które grają ładnie z wszelkiego rodzaju innym kodem. Korzystanie z std::unique_ptr zapewnia bardziej prymitywny interfejs i umożliwia śledzenie szczegółów (takich jak rozmiar) osobno. Dlatego też, z wyjątkiem innych ograniczeń, wolałbym std::vector.

6

Jeśli jesteś w pozycji, w której vector<int> jest możliwe, prawdopodobnie chcesz to zrobić z wyjątkiem ekstremalnych i rzadkich okoliczności.I nawet wtedy niestandardowy typ zamiast unique_ptr<int[]> może być najlepszą odpowiedzią.

Co jest, do cholery, dobre dla unique_ptr<int[]>? :-)

unique_ptr<T[]> naprawdę świeci w dwóch przypadkach:

1. trzeba obsłużyć malloc/free zasób z jakiejś funkcji starszego i chcesz to zrobić w bezpieczny styl nowoczesny wyjątek:

void 
foo() 
{ 
    std::unique_ptr<char[], void(*)(void*)> p(strdup("some text"), std::free); 
    for (unsigned i = 0; p[i]; ++i) 
     std::cout << p[i]; 
    std::cout << '\n'; 
} 

2. masz potrzeby tymczasowo zabezpieczyć nową [] zasobu przed przeniesieniem go do innego właściciela:

class X 
{ 
    int* data_; 
    std::string name_; 

    static void validate(const std::string& nm); 
public: 
    ~X() {delete [] data_;} 

    X(int* data, const std::string& name_of_data) 
     : data_(nullptr), 
      name_() 
    { 
     std::unique_ptr<int[]> hold(data); // noexcept 
     name_ = name_of_data;    // might throw 
     validate(name_);     // might throw 
     data_ = hold.release();    // noexcept 
    } 
}; 

W powyższym scenariuszu X jest właścicielem wskaźnika przekazanego do niego, niezależnie od tego, czy konstruktor się powiedzie, czy nie. Ten konkretny przykład zakłada domyślny konstruktor noexcept dla std::string, który nie jest zalecany. Jednak:

  1. Ten punkt można uogólnić na okoliczności, które nie dotyczą std::string.
  2. Domyślny konstruktor, który rzuca, jest kulawy.
Powiązane problemy