2012-11-06 18 views
10

Czy to jakoś możliwe, aby wykonać następujące czynności:Ukryj realizacja za pomocą wskaźnika (Pimpl idiom)

x.hpp - plik ten zawiera wiele innych klas

class x_impl; //forward declare 
class x { 
    public: 
     //methods... 
    private: 
     x_impl* impl_; 
}; 

x .cpp - realizacja

#include <conrete_x> 
typedef concrete_x x_impl; //obviously this doesn't work 
//implementation of methods... 

Więc zasadniczo chcę użytkownikom dołączyć plik x.hpp, ale nie zdawaj sobie sprawy z nagłówka conrete_x.hpp.

Ponieważ mogę używać concrete_x tylko przez wskaźnik i pojawia się tylko jako prywatny element danych, deklaracja przekazania powinna wystarczyć kompilatorowi, aby wiedzieć, ile miejsca na przygotowanie się do niego. Wygląda całkiem podobnie do dobrze znanego "idiomu pimpl".

Czy możesz mi w tym pomóc?

PS. Nie chcę używać void* i rzutować go wokół ..

+0

dlaczego nie możesz mieć concrete_x dziedziczą z x_impl? – StoryTeller

+0

Jaki jest dokładnie twój problem? – Grizzly

+0

Dlaczego nie chcesz zdefiniować samej klasy "class x_impl" w ? –

Odpowiedz

9

Właściwie, to nawet możliwe, aby całkowicie ukryć przed użytkownikiem:

// Foo.hpp 
class Foo { 
public: 

    //... 

private: 
    struct Impl; 
    Impl* _impl; 
}; 

// Foo.cpp 
struct Foo::Impl { 
    // stuff 
}; 

Chciałbym tylko przypomnieć, że:

  • trzeba będzie napisać właściwy destruktor
  • , a zatem będziesz potrzebował również właściwego konstruktora kopiowania, operatora przypisania kopiowania, konstruktora ruchu i operatora przeniesienia

Istnieją sposoby automatyzacji PIMPL, kosztem jakiejś czarnej magii (podobnej do tego, co robi std::shared_ptr).

+0

Proszę spojrzeć na pomysł '@Bart van Ingen Schenau'. Jakie jest twoje lepsze rozwiązanie? – emesx

+1

@elmes: Nazwa 'Impl' jest całkowicie ukryta, podczas gdy rozwiązanie Barta wprowadza nazwę' x_impl' w otaczającym obszarze nazw. Jest jeszcze bardziej ukryty dzięki strukturze "prywatnej". –

+0

Czy to działa, jeśli struct znajduje się w przestrzeni nazw w pliku cpp? – ChaoSXDemon

2

To będzie działać tylko wtedy, gdy deklaracja terminowa zadeklaruje faktyczną nazwę klasy. Więc albo zmienić x.hpp do:

class concrete_x; 
class x { 
    public: 
     //methods... 
    private: 
     concrete_x* impl_; 
}; 

lub użyć nazwy x_impl dla klasy określonej w nagłówku <concrete_x>.

0

Po to są interfejsy. Zdefiniuj interfejs (czysta klasa wirtualna) w udostępnianym pliku nagłówkowym i przekaż go użytkownikom. Dziedzicz swoją konkretną klasę z interfejsu i umieść ją w niepodzielonym pliku nagłówkowym. Zaimplementuj konkretną klasę w pliku cpp (możesz nawet zdefiniować konkretną klasę wewnątrz cpp).

+0

To wydaje się w porządku, tylko że będę miał wirtualną klasę tylko po to, aby ukryć szczegóły prywatnego członka; C++ absurd. – emesx

+0

@elmes, jeśli myślisz o możliwościach różnych betonów, niż jest to najbardziej naturalne podejście ... – StoryTeller

4

Jako alternatywę dla odpowiedzi z @Angew, jeśli nazwa concrete_x nie powinny być znane użytkownikom klasy X, można to zrobić:

w x.hpp

class x_impl; 
class x { 
    public: 
    x(); 
    ~x(); 
    //methods... 
    private: 
    x_impl* impl_; 
}; 

w x.CPP

#include <concrete_x> 
class x_impl : public concrete_x { }; 

x:x() : impl_(new x_impl) {} 
x:~x() { delete impl_; } 
+0

Tak, to jest rozwiązanie. Nie jest to jednak uniwersalne w C++ 11, ponieważ 'concrete_x' może być ostateczne. Ostatnie pytanie: jaki jest koszt wydajności/pamięci wyprowadzenia klasy, aby ją ukryć? – emesx

+0

W porównaniu do bezpośredniego zapisywania wskaźnika na concrete_x, nie ma kosztów wydajności/pamięci. Tylko (mały) koszt utrzymania, ponieważ opiekun musi to zrozumieć. –

+0

Czy możesz wyjaśnić, co się dzieje z 'concrete_x'? Brakuje przykładowego nagłówka i nie jest dla mnie jasne, dlaczego uwzględnienie jest obecne w 'x.cpp' kontra dodanie definicji klasy' konkretny_x' do początku 'x.cpp'. – jww

Powiązane problemy