2011-12-23 9 views
8

Patrz: my first attempt at answering this . Zaniedbałem opowiadanie całej historii wcześniej, próbując uprościć rzeczy. Okazuje się, że mój przykład działa! Przepraszam.Jak ustawić głównego przyjaciela mojej klasy z poziomu biblioteki?

Cała historia jest taka, że ​​jest to biblioteka zawierająca klasę w jednym pliku i główną w innym pliku, wszystkie połączone z moją biblioteką. Biblioteka stanowi podstawę dla Proces Framework, dlatego głównym jest w bibliotece, a nie proces.

Poniżej znajduje się obnażona wersja tego, co mam.

pf.hpp

using namespace std; 

namespace MyNamespace 
{ 
    class ProcessManager 
    { 
    public: 
    friend int main(int argc, char** argv); 
    private: 
    void test(); 
    }; 
}; 

pf.cpp

#include "pf.h" 

namespace MyNamespace 
{ 
    ProcessManager::test() 
    { 
    cout << "My friend has accessed my member" << endl; 
    } 
}; 

pfmain.cpp

#include "pf.hpp" 

int main(int argc, char** argv) 
{ 
    ProcessManager pm; 

    pm.test(); 
} 

Zauważ, że to się nie powiedzie na kompilacji biblioteki

Co ja próbowałem to:

  • Przesuwanie znajomego wszędzie
  • odsyłającego przyjaciela do zasięgu globalnym głównym zastosowaniem (np :: główne) Przyjaciel
  • Making a główne deklaracje użyć zasięgu globalnym

Czego mi brakuje?

Dzięki!

+1

To trochę nietypowy temat, ale wiele osób powiedziałoby, że słowo kluczowe "przyjaciel" nigdy nie jest dobrym rozwiązaniem. – asveikau

+0

To nie jest idealne Zgadzam się, ale czasami nie masz innej dobrej alternatywy. – Jaime

+0

Może chciałbym rozważyć inne przyjazne dla siebie warianty 'main()'. Nie jestem pewien, czy są na Facebooku. –

Odpowiedz

9

Wystarczy zadeklarować główną poza MyNamespace i określić globalną przestrzeń nazw :: w rachunku znajomego

//in header file of ProcessManager 
//your pf.h 

int main(int argc, char** argv); 

namespace MyNamespace 
{ 
    class ProcessManager 
    { 
    public: 
    friend int ::main(int argc, char** argv); 
    private: 
    void test(); 
    }; 
}; 
+0

Nie widzę powodu, dla którego kompilator nie zobaczy głównej z pfmain.cpp w moim przypadku, ale to działa. Dzięki! – Jaime

+0

Poprzednie próby pokazywałyby błąd "error:" int main (int, char **) "powinien zostać zadeklarowany wewnątrz '::'", co powinno być wystarczająco jasne, ale nigdy bym się nie spodziewał, że kompilator nie sprawdzi inne pliki .o dla symbolu, biorąc pod uwagę sposób ustawiania nagłówków. – Jaime

+0

@Jaime w C++ main() musi być zadeklarowane w globalnej przestrzeni nazw. Nazwa nie jest jednak zarezerwowana i może być używana dla funkcji składowych oraz w przestrzeniach między innymi. –

0

Wspólna odpowiedź może być zapewnienie „Aplikacja” klasę singleton, jak na przykład QApplication w Qt, zmniejszyć main coś jak

int main (int argc, char** argv) { 
    YourApplication app (argc, argv); 
    return app.execute(); 
} 

Następnie zmniejszyć friendness troskę o class YourApplication vs swoimi innych klas, a wiesz, jak to zrobić.

+0

Klasa jest właściwie singletonem, po prostu nie pokazałem tej części. Posiadanie singletonu nie rozwiązuje problemu .. – Jaime

0

Nie sądzę, że rzeczywiście chcesz robić to, co robisz. To naprawdę wygląda jak włamanie i problem z projektowaniem. Jeśli naprawdę chcesz odsłonić elementy wewnętrzne swojej klasy w pewnych szczególnych okolicznościach, możesz utworzyć klasę akcesorów, która jest również zdefiniowana w twojej bibliotece.

Coś takiego może działać (może być konieczne odpowiednie deklaracje forward itp - to tylko punkt wyjścia):

class ProcessManagerAccessor 
{ 
public: 
    ProcessManagerAccessor(ProcessManager & pm) : pm_(pm) { } 

    // add public methods to expose internals 
    void test() { pm_.test(); } 

private: 
    ProcessManager & pm_; 
}; 

class ProcessManager 
{ 
public: 
    friend class ProcessManagerAccessor; 

    // ... 
}; 

// ... 

ProcessManager pm; 
ProcessManagerAccessor pma(pm); 
pma.test(); 
+0

"Ogólnie rzecz biorąc, nie chcesz łamać żadnych enkapsulacji dla celów testowania (lub jak mówiła mama," nie wystawiaj swoich szeregowców! "). Przez większość czasu powinieneś być w stanie przetestować klasę, korzystając z jej publicznych metod. Jeśli istnieje znaczna funkcjonalność, która jest ukryta za prywatnym lub chronionym dostępem, może to być ostrzeżenie, że istnieje inna klasa, która stara się wyjść. " – bobbymcr

+0

Nie uważam tego za coś innego niż przyznanie głównego dostępu. Problem polega na tym, że moja klasa to Singleton, utworzona bez params, ale muszę ustawić jakiś stan w głównym, zanim przekażę kontrolę nad pochodną klasą ProcessManager. Nie chcę, aby te metody były dostępne dla klasy pochodnej. – Jaime

+0

Metoda "testowa" to tylko przykład ... nie jest ona faktycznie używana do testowania. – Jaime

2

@parapura dostarcza rozwiązanie, ale nie wyjaśnia, dlaczego ty najpierw trzeba zadeklarować main w zasięgu globalnym.

§7.3.1.2 [namespace.memdef] p3

[...] If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. [...]

więc mając to na uwadze, Twój kod będzie wyglądał mniej więcej tak:

namespace MyNamespace 
{ // MyNamespace is the innermost enclosing namespace 
    // 'main' from the friend declaration is treated 
    // as if it was a member of 'MyNamespace' 
    int main(int argc, char** argv); 

    class ProcessManager 
    { 
    public: 
    friend int main(int argc, char** argv); 
    private: 
    void test(); 
    }; 
}; 

Teraz powinno być jasne, dlaczego globalny main funkcja nie był twoim przyjacielem.

Powiązane problemy