2012-04-13 6 views
6

Napisałem funkcję, która ładuje bajty z pliku i zwraca strukturę FileData, która zawiera bufor bajtowy i długość bufora.Używanie inteligentnych wskaźników w struct lub class

Chcę, aby bufor został usunięty, gdy tylko zostanie zużyty i wyrzucony poza zakres.

Mam problem z jego kompilacją z powodu różnych błędów rzutowania. Ponadto nie jestem pewien, czy bufor jest przenoszony poprawnie, a nie kopiowany. Nie mam nic przeciwko kopiowaniu samej struktury FileData, ponieważ może to być najwyżej 16 bajtów.

Ogólnie, jak używać inteligentnych wskaźników jako pól klasy/struktury? Czy to nawet coś, co zrobiłbyś?

To trochę mgliste pytanie, ale ponieważ mam pewne konceptualne trudności z inteligentnymi wskaźnikami, mam nadzieję, że ten przykład pomoże mi we właściwym kierunku.

Oto co mam do tej pory:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    unsigned int len; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    char* buf = new char[len]; 

    str.read(buf, len); 
    str.close(); 

    FileData d = { unique_ptr<char[]>(buf), len }; 

    return d; 
} 

Edit: Ponieważ niektórzy ludzie są ciekawi komunikatu o błędzie, że otrzymuję z tego aktualnego kodu, to jest tutaj:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 
+0

Twój problem polega na tym, że nie podajesz żadnych szczegółowych informacji o błędach. Jak, na ziemi, możemy je zidentyfikować? – Puppy

+0

@DeadMG Założę się, że jest oczywiste, że są problemy z kodem, ponieważ wskazałem, że nie jestem pewien, czy jest to właściwy sposób używania inteligentnych wskaźników i przenoszenia semantyki. Chciałbym, aby kod robił więcej niż kompilację; Chciałbym, żeby to było poprawne i idiomatyczne. Niemniej jednak zaktualizowałem to pytanie, wyświetlając komunikat o błędzie. –

+0

Błąd, który otrzymujesz, ponieważ próbujesz skopiować unique_ptr, musisz użyć std :: move. Mogłeś użyć shared_ptr i zadeklarować swój własny deallocator, ale rozwiązanie wektorowe jest znacznie czystsze. – pstrjds

Odpowiedz

5

Kod jest w porządku, z wyjątkiem jednego małego szczegółu:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    <del>unsigned int</del> <ins>streamoff</ins> len; 
}; 

Powodem nie skompilować dla Ciebie jest to, że kompilator nie implementuje jeszcze automatycznego generowania specjalnych elementów przenoszenia. W pełni C++ 11 zgodnego kompilatora Twój FileData będzie zachowywać się tak, jakby:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&&) = default; 
    FileData& operator=(FileData&&) = default; 
    FileData(const FileData&) = delete; 
    FileData& operator=(const FileData&) = delete; 
    ~FileData() = default; 
}; 

ze standardowym konstruktor ruch wystarczy przesunąć konstruuje każdy członek (i podobnie dla niewykonaniem przypisanie ruchu).

Po przywróceniu d z LoadFile następuje niejawny ruch, który będzie wiązał się z domyślnie domyślnym konstruktorem ruchu.

Używanie vector<char> lub zgodnie z sugestią innych osób również zadziała. Ale nie ma nic złego w twoim kodzie, jeśli chodzi o C++ 11.

Och, mogę dostosować go tak: chciałbym, aby moje zasoby własność tak szybko, jak to możliwe:

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d = {unique_ptr<char[]>(new char[len]), len}; 

    str.read(d.buf.get(), d.len); 
    str.close(); 

    return d; 
} 

Jeśli trzeba wyraźnie określić FileData członków poruszać, powinno to wyglądać tak:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&& f) 
     : buf(std::move(f.buf)), 
      len(f.len) 
     { 
      f.len = 0; 
     } 

    FileData& operator=(FileData&& f) 
    { 
     buf = std::move(f.buf); 
     len = f.len; 
     f.len = 0; 
     return *this; 
    } 
}; 

Och, co prowadzi mnie do innego punktu. Domyślne elementy przenoszenia nie są poprawne, ponieważ nie ustawiają wartości len na 0 w źródle. To zależy od twojej dokumentacji, jeśli jest to błąd. ~FileData() nie wymaga len, aby odzwierciedlić długość bufora. Ale inni klienci mogą. Jeśli zdefiniujesz przeniesiony z FileData jako nie posiadający niezawodnego len, domyślne elementy przenoszenia są w porządku, w przeciwnym razie nie są.

+0

'FileData (FileData &&) = default;' - jest to skrócona składnia działająca na niektórych kompilatorach? Kiedy próbuję samodzielnie utworzyć konstruktor ruchu, narzeka on, że 'operator =' jest niedostępny. Hmm ... (VC++ 11, nawiasem mówiąc) –

+0

@ReiMiyasaka: Tak, składnia '= default' to sposób jawnego żądania tego, co w C++ 98/03 można nazwać jako domyślnie wygenerowany element specjalny . Działa dla domyślnego ctor, copy ctor, copy assignment, move ctor, move assignment i dtor. Ale może nie zostać jeszcze zaimplementowane (nie znam VC++ 11). Funkcje te zostały znormalizowane pod koniec gry, więc zrozumiałe jest, że Twój kompilator może ich jeszcze nie mieć. Składnia "= delete" jest w przybliżeniu równoznaczna z deklarowaniem jej jako prywatną i jej nie definiowania. Nie jestem pewien, co się dzieje z twoim jawnym ctorą ruchu. –

2

Prawdopodobnie użyłbym std::vector zamiast std:::unique_ptr<char[]>, jeśli nie przeszkadza ci skopiowanie std::vector po zwrocie:

struct FileData 
{ 
    vector<char> buf; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d; 
    d.buf.resize(len); 

    str.read(&(d.buf)[0], len); 
    str.close(); 

    return d; 
} 

Alternatywnie, aby uniknąć kopiowania, rozmówca może przejść w FileData jako parametr funkcji zamiast wartości zwracanej:

struct FileData 
{ 
    vector<char> buf; 
}; 

void LoadFile(string filename, FileData &data) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    data.buf.resize(len); 

    str.read(&(data.buf)[0], len); 
    str.close(); 
} 
+0

Czy to nie prowadzi do skopiowania samej "tablicy" danych pliku, czy też wektor implementuje semantykę ruchu C++ 11? Nie próbuje być kłótliwy, po prostu ciekawy. – pstrjds

+0

W takim przypadku nie potrzebowałbym nawet struktury FileData, więc odpowiedzi na połowę mojego pytania. Dzięki. Rozumiem, że wydajność nie jest taka zła przy zmianie rozmiaru wektora? –

+1

@Rei: Nie zmieni rozmiaru w podanym przykładzie. Ponieważ początkowo jest pusty, jest bardziej podobny do "rozmiaru": P i nie jest wolniejszy od samodzielnego przydzielania pamięci. – Puppy

-1

Jak o użyciu std :: string jako bufor. To wszystko zachowanie chcesz:

  • odniesienia liczone zamiast kopiować
  • znika raz z zakresu
  • posiadać dowolną ilość niepożądanego bajtów

Ludzie w dół głosu tym, ponieważ jej nie pierwotne zamierzone użycie ciągu; może czerpać klasę (lub owinąć go) i nazywają to „bufor”

+0

Myślę, że to zadziała, ale znowu, to nie pomaga mi zrozumieć, w jaki sposób używałbym inteligentnych wskaźników w innych podobnych sytuacjach. –

+0

Nie wiem, unique_ptr (używam doładowania), ale unique_ptr wygląda mi nie tak, zazwyczaj ptrs macie własne klasy. Chciałbym mieć uniq_ptr . Zasadniczo powinno zadziałać – pm100

+0

Kiedy "std :: string" zostało zliczone jako odniesienie?Czy jest to specyficzne dla implementacji w różnych kompilatorach? –

Powiązane problemy