2012-06-04 14 views
16

Czy istnieje sposób, aby uniknąć powtarzania Graph:: w pliku implementacji, a mimo to podzielić klasę na nagłówek + implementację? Takich jak:Czy można uniknąć powtarzania nazwy klasy w pliku implementacji?

nagłówku pliku: Plik

#ifndef Graph_H 
#define Graph_H 

class Graph { 
public: 
    Graph(int n); 
    void printGraph(); 
    void addEdge(); 
    void removeEdge(); 
}; 

#endif 

Realizacja:

Graph::Graph(int n){} 
void Graph::printGraph(){} 
void Graph::addEdge(){} 
void Graph::removeEdge(){} 
+1

To wygląda dobrze, zakładając, że nie zapomnisz "# include" nagłówka w implementacji. – dasblinkenlight

+3

Czy pytasz, czy możesz uniknąć wpisywania 'Graph ::' w implementacji? – GManNickG

+2

Wiem, że to prawda, ale pytam, czy jest inny sposób, bez konieczności powtarzania wykresu: .. –

Odpowiedz

7

Zgaduję, że ma to na celu uniknięcie "niepotrzebnego pisania". Niestety nie ma sposobu, aby pozbyć się zasięgu (jak wiele innych odpowiedzi ci powiedziała) jednak to, co robię osobiście, to klasa zdefiniowana we wszystkich moich prototypach funkcji w ładnych wierszach, a następnie skopiuj/wklej do pliku implementacji, a następnie ctrl-c your ClassName :: na tablicy klipów i uruchom linię za pomocą ctrl-v. .

9

Jeśli chcesz uniknąć wpisując "Graph ::" przed printGraph, addEdge itd., wtedy odpowiedź brzmi "nie", niestety. Funkcja "partial class" podobna do C# nie jest dostępna w C++, a nazwa dowolnej klasy (np. "Graph") nie jest przestrzenią nazw, jest zasięgiem.

+0

+1 za dwa inspirujące sposoby patrzenia na ograniczenia –

2

Nie, nie da się tego uniknąć. W przeciwnym razie, skąd wiadomo, że dana definicja funkcji dotyczy funkcji klasy lub funkcji statycznej?

+0

Jesteś oczywiście poprawny dla obecnego Standardu, ale chciałbym udawać, że mówisz hipotetycznie ;-) Tak jak na moje komentarze do OP: Łatwo się rozróżnić, używając proponowanej 'namespace class' do zdefiniowania członków: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0223r0.html - i wow, mam nadzieję, że to jest przyjęte ... i jestem bardzo smutny, wydaje się, że brakowało C++ 17 :(Jeśli ktokolwiek oceni ten banalny na zasadzie DRY, pomyśl o (A) intuicyjnym podobieństwie do normalnej "przestrzeni nazw", a zwłaszcza (B) implementacji funkcji 'template' poza linią, co jest rozdzierające. –

4

Nie, nie ma. Nie bezpośrednio. Możesz użyć sztuczek preprocesora, ale nie rób tego.

#define IMPL Graph:: 

IMPL Graph(int n){} 
void IMPL printGraph(){} 
void IMPL addEdge(){} 
void IMPL removeEdge(){} 

Ponadto, nie należy nawet chcą zrobić. Jaki jest sens. Poza tym, że jest to reguła C++, daje ci znać, że faktycznie implementujesz funkcję członka.

+1

Eh, czy to naprawdę mądre powiedzenie "Nie rób tego ... oto jak!"? – GManNickG

+2

@GManNickG Uważam, że tak, tak. Osobiście lubię znać te rzeczy ... –

+3

Można również zdefiniować je w osobnym pliku i "# include" je w treści klasy. – Pubby

2

Jeśli pytasz, czy możesz zdefiniować funkcję składową, taką jak Graph::printGraph bez określania kwalifikacji do klasy, wówczas odpowiedź brzmi "nie", a nie tak, jak chcesz. Nie jest to możliwe w C++:

plik realizacja:

void printEdge(){}; 

Powyższy skompiluje dobrze, ale to nie będzie robić, co chcesz. Nie zdefiniuje funkcji członka o tej samej nazwie w klasie Graph. Zamiast tego zadeklaruje i zdefiniuje nową bezpłatną funkcję o nazwie printEdge.

Jest to dobre i właściwe, jeśli z twojego punktu widzenia trochę bólu, ponieważ po prostu może chcesz dwie funkcje o tej samej nazwie, ale w różnych zakresach. Rozważ:

// Header File 
class A 
{ 
    void foo(); 
}; 

class B 
{ 
    void foo(); 
}; 

void foo(); 

// Implementation File 
void foo() 
{ 
} 

Do jakiego zakresu należy się stosować? C++ nie ogranicza możliwości posiadania różnych funkcji o tych samych nazwach w różnych zakresach, więc musisz powiedzieć kompilatorowi, jaką funkcję definiujesz.

0

EDIT: misread pytanie :(to byłaby odpowiedź na pytanie, czy można podzielić nagłówka plików przykro

Prosta odpowiedź: można podzielić C++ - plik, ale nie można podzielić do plików nagłówkowych.

Powód jest dość prosty. Ilekroć kompilator musi skompilować konstruktor, musi dokładnie wiedzieć, ile pamięci musi przeznaczyć na taki obiekt.

np

class Foo { 
    double bar; //8 bytes 
    int goo; //4 bytes 
} 

'nowe Foo()' wymaga przydział pamięci 12 bajtów. Ale jeśli pozwolono ci rozszerzyć definicje klas na wiele plików (tzn. Podzielić pliki nagłówkowe), łatwo by się z tym bałagan. Twój kompilator nigdy by się nie dowiedział, gdybyś powiedział mu wszystko na temat klasy, czy też tego nie zrobiłeś. Różne miejsca w kodzie mogą mieć różne definicje twojej klasy, co prowadzi do błędów segmentacji lub bardzo tajemniczych błędów kompilatora.

Na przykład:

h1.h:

class Foo { 
    double bar; //8 bytes 
    int goo; //4 bytes 
} 

h2.h: include "h1.h"

class Foo { 
    double goo; //8 bytes 
}// we extend foo with a double. 

foo1.cpp:

#include "foo1.h" 

Foo *makeFoo() { 
    return new Foo(); 

} 

foo2.cpp:

#include "foo2.h" 

void cleanupFoo(Foo *foo) { 
    delete foo; 
} 

foo1.h:

#include "h1.h" 

Foo *makeFoo(); 

foo2.h:

#include "h1.h" 
#include "h2.h" 

void cleanupFoo(Foo *foo) 

main.cpp:

#include foo1.h 
#include foo2.h 

void main() { 
    Foo *foo = makeFoo(); 
    cleanupFoo(foo); 
} 

Teraz bardzo uważnie sprawdzić, co się dzieje, jeśli najpierw skompilować main.cpp do main.o, następnie foo1.cpp do foo1.o i foo2.cpp do foo2.o, i fi połączyć wszystkie razem. To powinno się skompilować, ale makeFoo() przydziela coś innego, niż cleanallFoo() deallocated.

Więc nie ma, podziel się plikami .cpp, ale nie dziel się klasami na pliki nagłówkowe.

1
 //yes it is possible using preprocessor like this: 

     #define $ ClassName //in .cpp 

    void $::Method1() 
    { 
    } 

    //or like this: in the header .h: 

     #undef $ 
     #define $ ClassName' 

// but you have to include the class header in last #include in your .cpp: 

     #include "truc.h" 
     #include "bidule.h" ... 
     #include "classname.h" 

     void $::Method() { } 

     //i was using also 
     #define $$ BaseClass 
     //with single inheritance than i can do this: 

     void $::Method() 
     { 
     $$::Method(); //call base class method 
     } 

     //but with a typedef defined into class like this it's better to do this: 
     class Derived : Base 
     { 
     typedef Base $$; 

    } 
Powiązane problemy