2011-01-21 7 views
11

I jak tworzenie wektorów o określonej wielkości i wartości, na przykład w następujący sposób:Jak mogę zainicjować wektor std :: z parametrem wielkości i czy każdy obiekt jest tworzony niezależnie?

std::vector<std::string> names(10); 

jednak to kilka razy w tym prowadzi do nieoczekiwanych wyników. Na przykład w poniższym kodzie każdy UniqueNumber okazuje się mieć taką samą wartość:

#include <iostream> 
#include <string> 
#include <vector> 


struct UniqueNumber 
{ 
    UniqueNumber() : mValue(sInstanceCount++) 
    { 
    } 

    inline unsigned int value() const 
    { 
     return mValue; 
    } 

private: 
    static unsigned int sInstanceCount; 
    unsigned int mValue; 
}; 


int UniqueNumber::sInstanceCount(0); 


int main() 
{ 
    std::vector<UniqueNumber> numbers(10); 
    for (size_t i = 0; i < numbers.size(); ++i) 
    { 
     std::cout << numbers[i].value() << " "; 
    } 
} 

Console wyjście:

0 0 0 0 0 0 0 0 0 0 

To ma sensu, gdy patrząc na konstruktora std :: vector w:

explicit vector(size_type __n, 
       const value_type& __value = value_type(), 
       const allocator_type& __a = allocator_type()); 

Podobno wektor został zainicjowany kopiami tego samego obiektu.

Czy istnieje również sposób na skonstruowanie każdego obiektu domyślnego?

Odpowiedz

4

generate lub generate_n zostały zaprojektowane specjalnie dla tego, co chcesz robić. Oto pełna przykład:

#include <algorithm> 
#include <vector> 
#include <iterator> 
#include <iostream> 
using namespace std; 

class Foo 
{ 
public: 
    Foo() : n_(++water_) {}; 
    operator unsigned() const { return n_; } 
private: 
    static unsigned water_; 
    unsigned n_; 
}; 

Foo make_foo() 
{ 
    return Foo(); 
} 

unsigned Foo::water_ = 0; 

int main() 
{ 
    vector<Foo> v_foo; 
    generate_n(back_inserter(v_foo), 10, &make_foo); 
    copy(v_foo.begin(), v_foo.end(), ostream_iterator<unsigned>(cout, " ")); 
} 

wyjściowa: 1 2 3 4 5 6 7 8 9 10

+0

Dzięki za oferowanie nieinwazyjnego rozwiązania. – StackedCrooked

+0

@StackedCrooked, właściwie to jest uciążliwe, wolałbym używać obiektu do 'make_foo', byłoby znacznie lepiej. –

3

Trzeba dodać konstruktor kopiujący:

UniqueNumber(const UniqueNumber& un) : mValue(sInstanceCount++) 
{ } 

Konstruktor napełnienia std::vector nie dzwoni domyślnego konstruktora. Raczej wywołuje domyślny konstruktor kopiowania. Ten konstruktor oczywiście nie zwiększy wewnętrznej statycznej zmiennej licznika.

Należy również zdefiniować operatora przypisania.

Jednak użycie obiektu, który ma wewnętrzny licznik statyczny z std::vector, spowoduje nieoczekiwane wyniki, ponieważ wektor może wewnętrznie skopiować - skonstruować/przypisać obiekt tak, jak uważa za właściwy. Może to kolidować z wymaganą tu semantyką kopiowania.

+0

+1 mnie bić do niej :) – Zevan

+0

dobrze, to wywołanie konstruktora domyślnego raz, a następnie wywołanie konstruktora domyślnego kopiowania dla każdego elementu to dodaje in. Wyniki mogą być nieco nieoczekiwane, ponieważ wartości zaczynają się od 1, a nie 0, jeśli tego nie zauważysz. –

+0

To by naprawiło moją próbkę, ale teraz kopia nie jest już identyczna z oryginałem. Myślę, że to raczej nieintuicyjne zachowanie. – StackedCrooked

4

Wektor jest kopią-konstruowanie elementów do inicjalizacji.

Spróbuj tego:

#include <iostream> 
#include <string> 
#include <vector> 


struct UniqueNumber 
{ 
    UniqueNumber(bool automatic = true) 
     : mValue(automatic?sInstanceCount++:Special) 
    { } 

    UniqueNumber(UniqueNumber const& other) 
     : mValue(other.mValue==Special?sInstanceCount++:other.mValue) 
    { } 

    unsigned int value() const 
    { 
     return mValue; 
    } 

private: 
    static int sInstanceCount; 
    unsigned int mValue; 
    static unsigned int const Special = ~0U; 
}; 


int UniqueNumber::sInstanceCount(0); 


int main() 
{ 
    std::vector<UniqueNumber> numbers(10,UniqueNumber(false)); 
    for (size_t i = 0; i < numbers.size(); ++i) 
    { 
     std::cout << numbers[i].value() << " "; 
    } 
} 
+0

Nie jestem pewien, dlaczego dodałeś dodatkowy argument boolean do konstruktora UniqueNumber, ale wyraźnie, twoja metoda jest najbezpieczniejsza (w porównaniu do innych propozycji). –

+0

Wartość logiczna jest taka, że ​​użytkownik może ją skonstruować jako unikalną lub specjalną (taką, która jest bez wartości). –

+0

+1 za kreatywność – StackedCrooked

4

Istnieją dwa sposoby na zrobienie tego.

Miłą i C++ 0x sposób, jest do skorzystania z listy initializers:

std::vector<UniqueNumber> vec = { UniqueNumber(0), UniqueNumber(1) }; 

Oczywiście problemem jest to, że trzeba je wymienić w całości.

w C++ 03, chciałbym po prostu użyć pętli:

std::vector<UniqueNumber> vec; 
vec.reserve(10); 
for (size_t i = 0; i != 10; ++i) { vec.push_back(UniqueNumber(i)); } 

Oczywiście może to doskonale się osadzony w funkcji konstruktora:

template <typename ValueType, typename Generator> 
std::vector<ValueType> generateVector(size_t size, Generator generator) 
{ 
    std::vector<ValueType> vec; 
    vec.reserve(size); 
    for (size_t i = 0; i != size; ++i) { vec.push_back(generator()); } 
    return vec; 
} 

Z NRVO kopanie w, chodzi o to samo, i pozwala określić swobodnie utworzone wartości.

To samo można osiągnąć dzięki STL generate_n w <algorithm>, a ja dołączę do niego składnię lambda.

std::vector<ValueType> vec; 
size_t count = 0; 
std::generate_n(std::back_inserter(vec), 10, [&]() { return Foo(++count); }); 

Proponowane przez @Eugen w komentarzach :)


Uwaga: wrt do „niespodzianka”, jeśli chcesz mieć unikalne elementy, chyba że vector nie jest najbardziej odpowiednia struktura danych. A jeśli faktycznie potrzebujesz vector, rozważę zawijanie w klasie, aby zapewnić, jako niezmiennik, że elementy są unikalne.

+0

Zadzwoń do mnie, miał zamiar zaproponować użycie 'std :: generate_n (std :: back_inserter (numbers), 10, ...);' z '...' being jest albo "normalnym" 'CreateUniqueNumber' funkcja lub lambda ... –

+0

@Eugen: Ah, myślałem krótko o tym, że już widziałem taką konstrukcję, ale nie pamiętałem, że była to funkcja STL! Oczywiście zawsze chodzi o to, jak przejść od prostego "na" do długiego, wietrznego wyrazu ... –

+0

Uważam, że generate_n jest bardziej ekspresyjny niż generyczny i nie ma długiego kręcenia, szczególnie w grze lambdy

Powiązane problemy