2012-09-09 18 views
27

Próbuję dowiedzieć się, jak używać std :: shared_ptr z niestandardowym deleterem. W szczególności używam go z SDL_Surface jako:Używanie niestandardowego deltera ze std :: shared_ptr

std::shared_ptr<SDL_Surface>(SDL_LoadBMP(....),SDL_FreeSurface); 

który kompiluje i działa dobrze. Chciałbym jednak wypróbować mój własny deleter i nie mogę się dowiedzieć, jak to zrobić. Dokumentacja dla SDL_FreeSurface znajduje się tutaj:

http://sdl.beuc.net/sdl.wiki/SDL_FreeSurface

w którym znajdę SDL_FreeSurface jest zadeklarowana jako:

void SDL_FreeSurface(SDL_Surface* surface); 

jako test, a idąc tą informacją, próbowałem następujące funkcje:

void DeleteSurface(SDL_Surface* surface) 
{ 
    std::cout << "Deleting surface\n"; 
    SDL_FreeSurface(surface); 
} 

jednak kompilacji z g ++ daje mi następujący błąd:

error: no matching function for call to 'std::shared_ptr<SDL_Surface>::shared_ptr(SDL_Surface*, <unresolved overloaded function type>)' 

Przyjrzałem się dokumentacji GNU dla implementacji gcc std :: shared_ptr, ale nie mam z tego wiele sensu. Co ja robię źle?

EDYCJA: Od tego czasu zawęziłem problem, ale pozostawi pierwotne pytanie powyżej. Co miałam klasa Gra, która, jeśli ja rozebrać go do podstawowej implementacji, było coś takiego:

class Game { 
    public: 
     /* various functions */ 
    private: 
     void DeleteSurface(SDL_Surface* surface); 
     bool CacheImages(); 
     std::vector<std::shared_ptr<SDL_Surface> > mCachedImages; 

     /* various member variables and other functions */ 
} 

z realizacji DeleteSurface jak powyżej, a realizacja CacheImages() jak:

bool CacheImages() 
{ 
    mCachedImages.push_back(std::shared_ptr<SDL_Surface>(SDL_LoadBMP(...),DeleteSurface); 
    return true; 
} 

w którą grę mam błąd, który wymieniłem powyżej. Jeśli jednak przestawię funkcję DeleteSurface() poza klasę Game, nie zmieniając jej, kod się skompiluje. Co się stało z włączeniem funkcji DeleteSurface w klasie Game, która powoduje problemy?

+0

Twój przykład kompiluje się dobrze dla mnie. –

Odpowiedz

42
std::shared_ptr<SDL_Surface>(SDL_LoadBMP(....), [=](SDL_Surface* surface) 
{ 
    std::cout << "Deleting surface\n"; 
    SDL_FreeSurface(surface); 
}); 

lub

void DeleteSurface(SDL_Surface* surface) 
{ 
    std::cout << "Deleting surface\n"; 
    SDL_FreeSurface(surface); 
} 

std::shared_ptr<SDL_Surface>(SDL_LoadBMP(....), DeleteSurface); 

EDIT:

Widząc zaktualizowaną pytanie, DeleteSurface powinna być funkcją non-członek, w przeciwnym razie trzeba użyć std::bind lub std::mem_fn lub jakąś inną funkcję składową adapter wskaźnika .

+0

Twój drugi przykład to to, co już miałem, a którego nie skompiluję. – Wheels2050

+0

@ Wheels2050: Kompiluje się dobrze, w tym przypadku zostawiasz coś ze swojego przykładu. – ronag

+0

Przepraszam, masz rację - zredagowałem moje pytanie. – Wheels2050

8

Ten kod stanowi przykład współdzielonej konstrukcji wskaźnika z deleterem jako metodą obiektową. Wyświetla instrukcję, której należy użyć.

Przykładem jest prosty recykler obiektów. Kiedy ostatnie odniesienie do obiektu zostanie zniszczone, obiekt jest zwracany do puli wolnych obiektów wewnątrz recyklera.

Recyler można łatwo zmienić na pamięć podręczną obiektów, dodając klucz do metod get() i add() i zapisując obiekty w postaci std::map.

class ObjRecycler 
{ 
private: 
    std::vector<Obj*> freeObjPool; 
public: 
    ~ObjRecycler() 
    { 
     for (auto o: freeObjPool) 
      delete o; 
    } 

    void add(Obj *o) 
    { 
     if (o) 
      freeObjPool.push_back(o); 
    } 

    std::shared_ptr<Obj> get() 
    { 
     Obj* o; 
     if (freeObjPool.empty()) 
      o = new Obj(); 
     else 
     { 
      o = freeObjPool.back(); 
      freeObjPool.pop_back(); 
     } 
     return std::shared_ptr<Obj>(o, 
      std::bind(&ObjRecycler::add, this, std::placeholders::_1)); 
    } 
} 
+1

Chciałem tylko powiedzieć, że to nie działa zgodnie z przeznaczeniem. Kiedy wywołasz 'get()', ** zawsze otrzymasz współdzielony wskaźnik z liczbą referencyjną równą '1'. Co gorsza, wiele współdzielonych wskaźników może wskazywać na to samo wspomnienie. Jeśli ktoś wykracza poza zakres, dostaniesz segfaults. To, czego potrzebujesz, to nie 'std :: vector ', ale 'std :: vector >'. – rwols

+0

Nie chcesz 'std :: vector >' ponieważ nie zachowa to czasu życia bufora, który chcesz ponownie użyć, chcesz 'std :: vector > zamiast tego. możesz implicicly stworzyć 'std :: shared_ptr' z twojego wcześniej istniejącego' std :: unique_ptr', a następnie niestandardowy deleter może następnie wziąć surowy wskaźnik teraz-unowned i umieścić go w nowym 'std :: unique_ptr' i umieścić do twojego 'std :: vector'. –

Powiązane problemy