Dodawanie plików do projektu
W miarę powiększania się programów często zdarza się dzielenie ich na wiele plików ze względów organizacyjnych lub umożliwiających ponowne użycie. Jedną z zalet pracy z IDE jest to, że znacznie ułatwia pracę z wieloma plikami. Wiesz już, jak tworzyć i kompilować projekty jednoplikowe. Dodawanie nowych plików do istniejących projektów jest bardzo proste.
Najlepsza praktyka
Kiedy dodajesz nowe pliki kodu do swojego projektu, nadaj im rozszerzenie .cpp.
W przypadku użytkowników programu Visual Studio
W Visual Studio kliknij prawym przyciskiem myszy folder Source Files (lub nazwę projektu) w oknie Eksploratora rozwiązań i wybierz Dodaj > Nowy element….

Upewnij się, że masz Plik C++ (.cpp) wybrano. Nadaj nowemu plikowi nazwę, a zostanie on dodany do Twojego projektu.
Uwaga: Twój Visual Studio może zdecydować się na pokazywanie widoku kompaktowego zamiast pełnego widoku pokazanego powyżej. Możesz użyć widoku kompaktowego lub kliknąć „Pokaż wszystkie szablony”, aby wyświetlić pełny widok.

Uwaga: Jeśli utworzysz nowy plik z menu Menu Plik zamiast z projektu w Eksploratorze rozwiązań, nowy plik nie zostanie automatycznie dodany do projektu. Będziesz musiał dodać go do projektu ręcznie. Aby to zrobić, kliknij prawym przyciskiem Source Files w klasie Eksplorator rozwiązań, wybierz Dodaj > Istniejący element, a następnie wybierz swój plik.
Teraz, kiedy kompilujesz swój program, powinieneś zobaczyć, jak kompilator wyświetla nazwę twojego pliku podczas jego kompilacji.
W przypadku użytkowników Code::Blocks
W Code::Blocks przejdź do menu Menu Plik i wybierz Nowy > Plik….

W Nowy z szablonu okno dialogowe, wybierz Źródło C/C++ i kliknij Przejdź.

W tym momencie może pojawić się okno powitalne <<<M26>>>powitanie w kreatorze pliku źródłowego C/C++ . Jeśli tak, kliknij Dalej.

Na następnej stronie kreatora wybierz „C++” i kliknij Dalej.

Teraz nadaj nowemu plikowi nazwę (nie zapomnij o rozszerzeniu .cpp) i kliknij przycisk Wszystkie , aby upewnić się, że wszystkie cele kompilacji zostały wybrane. Na koniec wybierz zakończ.

Teraz, kiedy kompilujesz swój program, powinieneś zobaczyć, jak kompilator wyświetla nazwę twojego pliku podczas jego kompilacji.
Dla użytkowników gcc
Z poziomu wiersza poleceń możesz samodzielnie utworzyć dodatkowy plik, używając swojego ulubionego edytora i nadać mu nazwę. Kiedy kompilujesz swój program, będziesz musiał dołączyć wszystkie odpowiednie pliki kodu do linii kompilacji. Na przykład: g++ main.cpp add.cpp -o main, gdzie main.cpp i add.cpp to nazwy plików z kodem i głównego to nazwa pliku wyjściowego.
Dla użytkowników VS Code
Aby utworzyć nowy plik, wybierz Widok > Eksplorator z górnego panelu nawigacyjnego, aby otworzyć panel Eksploratora, a następnie kliknij Nowy Ikona pliku po prawej stronie nazwy projektu. Alternatywnie wybierz Plik > Nowy plik z górnego panelu nawigacyjnego. Następnie nadaj swojemu nowemu plikowi nazwę (nie zapomnij o rozszerzeniu .cpp). Jeśli plik pojawi się w folderze .vscode , przeciągnij go o jeden poziom w górę do folderu projektu.
Następnie otwórz plik tasks.json i znajdź linię "${file}",.
Masz tutaj dwie możliwości:
- Jeśli chcesz wyraźnie określić, jakie pliki będą kompilowane, zamień
"${file}",nazwę każdego pliku, który chcesz skompilować, po jednej w następujący sposób:
"main.cpp","add.cpp",
- Reader „geo” raportuje, że VS Code może automatycznie skompilować wszystkie pliki .cpp w katalogu, zastępując
"${file}",z"${fileDirname}\\**.cpp"(w systemie Windows). - Reader „Orb” zgłasza, że
"${fileDirname}/**.cpp"działa w systemie Unix.
Przykład wielu plików
W lekcji 2.7 -- Deklaracje przesyłania dalej i definicje, przyjrzeliśmy się program jednoplikowy, który nie chce się skompilować:
#include <iostream>
int main()
{
std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n';
return 0;
}
int add(int x, int y)
{
return x + y;
}Kiedy kompilator osiągnie wywołanie funkcji add w linii 5 głównego, nie wie, co add jest, ponieważ zdefiniowaliśmy add aż do linii 9! Naszym rozwiązaniem była albo zmiana kolejności funkcji (umieszczenie add najpierw) albo użycie deklaracji forward dla add.
Przyjrzyjmy się teraz podobnemu programowi wieloplikowemu:
add.cpp:
int add(int x, int y)
{
return x + y;
}main.cpp:
#include <iostream>
int main()
{
std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n'; // compile error
return 0;
}Twój kompilator może najpierw skompilować add.cpp lub main.cpp . Tak czy inaczej, main.cpp nie uda się skompilować, co spowoduje ten sam błąd kompilatora, co w poprzednim przykładzie:
main.cpp(5) : error C3861: 'add': identifier not found
Powód jest dokładnie ten sam: kiedy kompilator dotrze do linii 5 main.cpp, nie wie, jaki identyfikator add jest.
Pamiętaj, kompilator kompiluje każdy plik indywidualnie. Nie wie o zawartości innych plików kodu ani nie pamięta niczego, co widział z wcześniej skompilowanych plików kodu. Zatem nawet jeśli kompilator mógł widzieć definicję funkcji add wcześniej (jeśli skompilował add.cpp najpierw), nie pamięta.
Ta ograniczona widoczność i krótka pamięć są zamierzone z kilku powodów:
- Pozwala ona na kompilację plików źródłowych projektu w dowolnej kolejności.
- Kiedy zmieniamy plik źródłowy, wystarczy zmienić tylko ten plik źródłowy rekompilowany.
- Redukuje to możliwość konfliktów nazw pomiędzy identyfikatorami w różnych plikach.
Zbadamy, co się dzieje, gdy nazwy powodują konflikt w następnej lekcji (2.9 -- Kolizje nazewnictwa i wprowadzenie do przestrzeni nazw).
Nasze opcje rozwiązania są tutaj takie same jak poprzednio: umieść definicję funkcji add przed funkcją głównego lub zadowalaj kompilator deklaracją forward. W tym przypadku, ponieważ funkcja add znajduje się w innym pliku, opcja zmiany kolejności nie jest możliwa.
Rozwiązaniem jest użycie deklaracji forward:
main.cpp (z deklaracją forward):
#include <iostream>
int add(int x, int y); // needed so main.cpp knows that add() is a function defined elsewhere
int main()
{
std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n';
return 0;
}add.cpp (pozostaje taki sam):
int add(int x, int y)
{
return x + y;
}Teraz, gdy kompilator będzie się kompilował main.cpp, będzie wiedział, co identyfikator add jest i będzie spełniony Linker połączy wywołanie funkcji add w main.cpp z definicją funkcji add w add.cpp.
Za pomocą tej metody możemy dać plikom dostęp do funkcji znajdujących się w innym pliku.
Spróbuj samodzielnie przeprowadzić kompilację add.cpp i main.cpp z deklaracją forward. Jeśli pojawi się błąd linkera, upewnij się, że tak dodany add.cpp do Twojego projektu lub linii kompilacji.
Wskazówka
Ponieważ kompilator kompiluje każdy plik kodu indywidualnie (a następnie zapomina, co widział), każdy plik kodu, który używa std::cout lub std::cin trzeba #include <iostream>.
W powyższym przykładzie, jeśli add.cpp użył std::cout lub std::cin, musiałby #include <iostream>.
Kluczowa informacja
Gdy w wyrażeniu używany jest identyfikator, identyfikator musi być podłączony do swojej definicji.
- Jeśli kompilator nie widział ani deklaracji forward, ani definicji identyfikatora w kompilowanym pliku, popełni błąd w momencie użycia identyfikatora.
- W przeciwnym razie, jeśli definicja istnieje w tym samym pliku, kompilator połączy użycie identyfikatora z jego definicją.
- W przeciwnym razie, jeśli definicja istnieje w innym pliku (i jest widoczna dla linkera), linker to zrobi połącz użycie identyfikatora z jego definicją.
- W przeciwnym razie linker zgłosi błąd wskazujący, że nie mógł znaleźć definicji identyfikatora.
Coś poszło nie tak!
Jest wiele rzeczy, które mogą pójść nie tak przy pierwszej próbie pracy z wieloma plikami. Jeśli wypróbowałeś powyższy przykład i napotkałeś błąd, sprawdź następujące elementy:
- Jeśli pojawi się błąd kompilatora. about add nie jest zdefiniowane w głównego, prawdopodobnie zapomniałeś deklaracji forward dla funkcji add w main.cpp.
- Jeśli pojawi się błąd linkera dotyczący add niezdefiniowania, np.
unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z) referenced in function _main
2a. Najbardziej prawdopodobną przyczyną jest to, że add.cpp nie jest poprawnie dodany do twojego projektu. Podczas kompilacji powinieneś zobaczyć listę kompilatora oba main.cpp i add.cpp Jeśli widzisz tylko main.cpp, wówczas add.cpp na pewno nie jest kompilowany. Jeśli używasz Visual Studio lub Code::Blocks, powinieneś zobaczyć add.cpp listę w panelu Eksplorator rozwiązań/projekt po lewej lub prawej stronie IDE. Jeśli nie, kliknij projekt prawym przyciskiem myszy i dodaj plik, a następnie spróbuj skompilować ponownie za pomocą polecenia wierszu, nie zapomnij uwzględnić obu main.cpp i add.cpp w poleceniu kompilacji.
2b ...możliwe, że dodałeś add.cpp do złego projektu.
2c. …możliwe, że plik jest ustawiony tak, aby się nie kompilował ani nie łączył. Sprawdź właściwości pliku i upewnij się, że plik jest skonfigurowany do kompilacji/linkowania. W Code::Blocks, kompilacja i link to osobne pola wyboru, które należy zaznaczyć. W Visual Studio dostępna jest opcja „wyklucz z kompilacji”, którą należy ustawić na „nie” lub pozostawić pustą. Upewnij się, że sprawdziłeś każdą konfigurację kompilacji (np. debugowanie i wydanie) osobno.
- Wykonaj nie #include „add.cpp” z main.cpp. Podczas kompilacji w tym przypadku #włączenie plików .cpp zwiększa ryzyko konfliktów nazewnictwa i innych nieprzewidzianych konsekwencji (zwłaszcza gdy programy stają się większe i bardziej złożone). #include omawiamy dalej w lekcji 2.10 — Wprowadzenie do preprocesora.
Streszczenie
C++ został zaprojektowany tak, aby każdy plik źródłowy mógł być kompilowany niezależnie, bez wiedzy o tym, co znajduje się w innych plikach. Dlatego też kolejność, w jakiej pliki są faktycznie kompilowane, nie powinna mieć znaczenia.
Po wejściu w programowanie obiektowe zaczniemy często pracować z wieloma plikami, więc teraz jest dobry moment, aby upewnić się, że wiesz, jak dodawać i kompilować wiele projektów plikowych.
Przypomnienie: za każdym razem, gdy tworzysz nowy plik z kodem (.cpp), musisz dodać go do swojego projektu, aby został skompilowany.
Czas quizu
Pytanie nr 1
Podziel poniższy program na dwa pliki (main.cpp i input.cpp). main.cpp powinien mieć funkcję main, a input.cpp powinien mieć funkcję getInteger.
#include <iostream>
int getInteger()
{
std::cout << "Enter an integer: ";
int x{};
std::cin >> x;
return x;
}
int main()
{
int x{ getInteger() };
int y{ getInteger() };
std::cout << x << " + " << y << " is " << x + y << '\n';
return 0;
}
