Do czego służy #ifndef, #define, #pragma once oraz #endif

0

Cześć, jestem w trakcie pisania mojego pierwszego "większego" projektu obiektowego w c++, podczas pisania natknąłem się na kilka problemów, googlowałem treść errorów i jednym z zaleceń było dodanie w plikach nagłówkowych .h następujących poleceń:

#include <iostream>
#include "Instrument.h"
#ifndef GITARA_H
#define GITARA_H

using namespace std;

#pragma once

class Gitara : public Instrument
{
private:
    int iloscStrun;
    string krajPochodzenia;
    string czyUzywany;

public:
    Gitara(string p, string m, int c, int ileS, string krajP, string czyUz);
    virtual void pokazInstrument();
    virtual void zapiszInstrument();

};

#endif

Czy ktoś mógłby mi w prosty sposób dlaczego potrzebuję #ifndef GITARA_H, #define GITARA_H, #pragma once oraz #endif

0

#ifndef - jeśli nie jest zdefiniowane to wykonaj
#define ....
reszta kodu
#endif

if (coś == null) // ifndef
{
 cos == objekt // define
} // endif

Tyle, że obliczy to preprocessor w czasie kompilacji.

0

A mógłbyś przełożyć to na język bardziej zrozumiały dla początkującego?

0

https://pl.wikibooks.org/wiki/C/Preprocesor

Musisz poczytać.
Jest w miarę sensowniej wytłumaczone, niż ja bym miał tu to zrobić.

1

Preprocesor, to narzędzie, które nie wie, że ma do czynienia z kodem.
Robi tylko to, co ma zrobić, czyli w przypadku C++
#define - zdefiniować pewną wartość. Np. #define PI 3.1431415926.... gdy preprocesor napotka potem w tekście słowo PI, zamieni je na 3.143...
#undef "oddefiniuj" wartość
#ifndef - jeżeli nie zostało zdefiniowane X
#endif - to zrób coś pomiędzy tym ifndef a endif
#pragma to sam nie wiem co jest
I tego jest wiele, wiele, wiele więcej.
http://www.cplusplus.com/doc/tutorial/preprocessor/
https://pl.wikipedia.org/wiki/CPP_(preprocesor)

4

Jeśli masz coś takiego:

#ifndef X
#define X
... DUŻO KODU
#endif

to jest to tak zw. include guard. Jest potrzebny dlatego, że #include powoduje dosłowne wstawienie treści danego pliku. W związku z tym może się zdarzyć, że dołączysz 2 razy ten sam plik i będziesz mieć np. powielone definicje funkcji, klas, zmiennych... Include guard zapobiega takim sytuacjom.
Jednak include guard ma tę wadę, że X musi być unikalne. Jeśli masz 2 pliki z takim samym include guard, to masz problem.

Zapobiega temu #pragma once - wstawione na początku pliku sprawia, że nie musisz robić include guarda i nie musisz się martwić nazwą makra, które definiujesz. #pragma once jest obsługiwane przez każdy kompilator, więc jego użycie jest zalecane.
Po prostu na początku każdego pliku nagłówkowego wstawiasz #pragma once i możesz includować go do woli :)

Ogólnie dyrektywy #pragma są furtką, która pozwala wprowadzać niestandardowe rozszerzenia do języka (zobacz np. OpenMP), ale akurat once jest tak powszechne, że nie musisz się tym przejmować.

Nawiasem mówiąc, preprocesor jest głupi i nie zna np. wartości zmiennych. Nie zrobisz więc czegoś takiego:

int a = 3;
#if a == 3
    cout << a <<endl;
#endif

Preprocesor zna tylko to, co definiujesz w jego obrębie, więc coś takiego już zadziałą:

#define A 3
#if A == 3
    cout << A << endl;
#endif

W miejsce A zostanie po prostu wstawione 3, ale A nie jest zmienną ani stałą. Po prostu A jest w kodzie bezpośrednio zastępowane trójką.

2

Dzięki takim ludziom to forum ma sens! Wielkie dzięki! Jakby ktoś chciał jeszcze dorzucić coś od siebie to proszę bardzo

4

Ja dorzucę, że using namespace std; (albo dowolne inne) w nagłówkach należy uważać za zbrodnię i absolutnie nie praktykować. Jest to ogólnie wątpliwa praktyka, ale w nagłówkach jest kompletnie zła. https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice

2
kq napisał(a):

Ja dorzucę, że using namespace std; (albo dowolne inne) w nagłówkach należy uważać za zbrodnię i absolutnie nie praktykować. Jest to ogólnie wątpliwa praktyka, ale w nagłówkach jest kompletnie zła. https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice

Pozwolę sobie uzupełnić, że jak ktoś leniwy i nie chce mu się pisać std:: za każdym razem to zamiast pisania using std::string można też wykorzystać dobrodziejstwo aliasów i pisać:

using String = std::string

Jest to o tyle fajne, że przyzwyczaja do aliasów i początkującemu łatwiej będzie korzystać z aliasów zamiast typedef, które notabene jest mocno upośledzone :)

0

Ja mam tylko jedno pytanie, czemu w zasadzie w C++ jest najsurowiej zakazane using namespace std;, a w C# using System; już nie? (A może using System; także w C# jest najsurowiej zakazane?)

Jakoś nigdy nie pojąłem sensu zakazywania using namespace std; do jednoplikowych programów. Po co?

@Tenonymous Czemu using String = std::string? Nie lepiej using std::string?

foo::bar::baz::fizz::buzz::blah::yadda::someClass varName(foo::bar::baz::fizz::buzz::blah::yadda::someEnum::SomeArg);
var varName = new Foo.Bar.Baz.Fizz.Buzz.Blah.Yadda.SomeClass(Foo.Bar.Baz.Fizz.Buzz.Blah.Yadda.SomeEnum.SomeArg);

Czy pełne kwalifikowanie każdego wyrażenia nie czyni kodu nieczytelnym?

0

using namespace std nie jest zakazane, tylko nie zaleca się stosowania tej dyrektywy w plikach nagłówkowych. W plikach cpp można stosować, ale też ostrożnie, zwłaszcza gdy korzystamy w danym cpp z różnych bibliotek.
Zresztą, zamiast using namespace std, może zrobić wybiórczo, np using std::cout.
W pokazanym przez Ciebie przykładzie można też zastosować alias:

namespace foo::bar::baz::fizz::buzz::blah::yadda fbbfbby
fbbfbby::someClass varName(fbbfbby::SomeEnum::SomeArg)

1 użytkowników online, w tym zalogowanych: 0, gości: 1