Jak podzielić program z klasami na wiele plików?

0

Mam ogromny problem, z którym walczę już drugi dzień i nie mogę za żadne skarby dojść do sedna błędu, byłbym ogromnie wdzięczny za pomoc w rozwiązaniu problemu, może ten przykład przyda się także innym gdyż jest bardzo ogólny... :(
Oto problem: Napisałem drobniutki program (typowy przykład do nauki języka obiektowego - a dokładnie z działu 'kompozycje'), program działa, po napisaniu poprawnego programu (program ma dwie klasy i jednego głównego main'a). Potrzebuję rozdzielić program tak, aby każda klasa (jest ich 2) była w oddzielnym pliku/plikach (.h oraz .cpp - nie wiem co dokładnie trzeba gdzie dodać - tzn w teorii wiem, lecz jak przekładam to na praktykę - program się sypie...) ... :( Pomóżcie :(

oto kod:

#include <iostream>
using namespace std;

class Birthday {
    public:
        Birthday(int m, int d, int y)
        : month(m), day(d), year(y)
        {  }
        void printDate()
        {
            cout<<month<<"/"<<day <<"/"<<year<<endl;
        }
    private:
        int month;
        int day;
        int year;
};

class Person {
    public:
        Person(string n, Birthday b)
        : name(n), bd(b)
        {  }
        void printInfo()
        {
            cout << name << endl;
            bd.printDate();
        }
    private:
        string name;
        Birthday bd;
};

int main() {
    Birthday bd(2, 21, 1985);
    Person p("David", bd);
    p.printInfo();
}

program ma dać na wyjściu

David
2/21/1985

Pozdrawiam, Biedny Wężu

4

Dzielisz program na deklaracje:

void printDate();

i definicje:

void Birthday::printDate()
{
    cout<<month<<"/"<<day <<"/"<<year<<endl;
}

Teraz wystarczy, że w pliku nagłówkowym .h umieścisz swoją klasę zawierajacą jedynie deklaracje:

class Birthday
{
    public:
        Birthday(int m, int d, int y);
        void printDate();

    private:
        int month;
        int day;
        int year;
};

a w pliku źródłowym .cpp definicje metod tej klasy:

Birthday::Birthday(int m, int d, int y)
        : month(m), day(d), year(y)
{
}

void Birthday::printDate()
{
    cout<<month<<"/"<<day <<"/"<<year<<endl;
}
0

A z klasą Person jak to podzielić? Bo tutaj Pan rozpisał to bardzo ładnie. Jeszcze jedno pytanko: odwołanie (include) muszę włączyć w których plikach z odwołaniem do których? Mógłby Pan lub ktoś inny przedstawić przykładową zawartość każdego pliku? Wiem, że dla programistów jest to banalne i oczywiste, lecz dla zielonego laika jest kłopot :/

1

Z klasą Person dokładnie tak samo, analogicznie: W pliku nagłówkowym tworzysz deklaracje klasy Person, w pliku źródłowym definicje jej metod:

#include "Person.h"

void Person::printInfo()
{
    cout << name << endl;
    bd.printDate();
}

Jak widzisz załączyłem w przykładzie plik nagłówkowy, który zawiera deklarację klasy Person, bez tego człon Person:: nie byłby rozpoznany w programie.
Co do przykładowej zawartości pliku Person.h to będzie ona analogiczna do przykładu z mojej poprzedniej odpowiedzi.

1

Przy czym należy pamiętać o tym, żeby w plikach *.h stosować mechanizm https://en.wikipedia.org/wiki/Include_guard ewentualnie #pragma once Z tym, że ten drugi moim zdaniem jest bardziej wskazany. Pierwszy stosujemy jeśli kompilator nie obsługuje dyrektywy pragma once.

0

Dziękuję Bartosz za pomoc, ale i tak nie umiem rozwiązać problemu... :/ Mógłbyś podać mi zawartość każdego pliku? Ja to robię w taki sposób i nie wychodzi (piszę w code blocks):

main.cpp:

#include <iostream>

using namespace std;

int main()
{
    Birthday bd(2, 21, 1985);
    Person p("David", bd);
    p.printInfo();

    return 0;
}

Birthday.cpp:

#include "Birthday.h"
#include <iostream>

Birthday::Birthday(int m, int d, int y)
        : month(m), day(d), year(y)
{
}

void Birthday::printDate()
{
    cout<<month<<"/"<<day <<"/"<<year<<endl;
}

Birthday.h:

#ifndef BIRTHDAY_H
#define BIRTHDAY_H


class Birthday
{
    public:
        Birthday(int m, int d, int y);
        void printDate();

    private:
        int month;
        int day;
        int year;
};

#endif // BIRTHDAY_H

Person.cpp:

#include "Person.h"
#include <string>
#include "Birthday.h"


void Person::printInfo()
{
    cout << name << endl;
    bd.printDate();
}

Person.h:

#ifndef PERSON_H
#define PERSON_H


class Person {
    public:
        Person(string n, Birthday b)
        : name(n), bd(b)
        {  }
        void printInfo()
        {
        }
    private:
        string name;
        Birthday bd;
};

#endif // PERSON_H

Bardzo proszę o pomoc i wstawienie dobrego kodu, fajnie by było jakby było z wyjaśnieniem, jak nie to też doceniam. Stawiam piwo za pomoc. Nie ma co ruszać dalej z programowaniem bez opanowania dzielenia programu na pliki... Proszę pomóżcie :( :( :(

1

main.cpp

#include "person.h" //wystarczy person.h, ponieważ w tym pliku jest zinkludowany birthday.h(w konstruktorze jest obiekt klasy birthday)

int main() {
	Birthday bd(2, 21, 1985);
	Person p("David", bd);
	p.printInfo();
}

person.h

#pragma once

#include "birthday.h"    //dodany, ponieważ konstruktor pobiera obiekt tej klasy oraz obiekt Birthday jest polem tej klasy
#include <string>
using namespace std;    //przestrzeni nazw potrzebuje string, więc dodajemy lub bezpośrednio przed zmienna: std::string n, std::string name, ale wtedy musisz std dodać również w pliku person.cpp

class Person
{
public:
	Person(string n, Birthday b);
	void printInfo();
private:
	string name;
	Birthday bd;
};

person.cpp

#include "person.h"
#include <iostream>  //dodajemy ze wzgledu na cout, przestrzen nazw std zostala dodana w person.h, więc załączając ją, załączamy sobie też użycie przestrzeni nazw std

Person::Person(string n, Birthday b) : name(n), bd(b)
{ }

void Person::printInfo()
{
	cout << name << endl;
	bd.printDate();
}

birthday.h

#pragma once

class Birthday 
{
public:
	Birthday(int m, int d, int y);
	void printDate();	
private:
	int month;
	int day;
	int year;
};

birthday.cpp

#include "birthday.h"
#include <iostream> // dodajemy ze względu na cout i dodajemy użycie przestrzeni nazw, ponieważ w pliku birthday.h nie występuje
using namespace std;

Birthday::Birthday(int m, int d, int y) : month(m), day(d), year(y)
{ }

void Birthday::printDate()
{
	cout << month << "/" << day << "/" << year << endl;
}

I ogólnie staraj się załączać biblioteki w plikach cpp, jeśli nie jest to konieczne w plikach h. ponieważ gdy np w mainie załączamy wiele plików h to często załączamy od razu niepotrzebne biblioteki co jest nieekonomiczne

1
Draaz napisał(a):

#include "person.h" //wystarczy person.h, ponieważ w tym pliku jest zinkludowany birthday.h(w konstruktorze jest obiekt klasy birthday)

Ja nigdy tak nie robię. Jeśli potrzebuję jakiegoś typu/klasy to zawsze dodaję nagłówek w którym jest ona zadeklarowana. Polegając na niejawnym dołączaniu nagłówków można się przejechać wcześniej czy później. Wystarczy, że person.h przestanie z jakichś względów dołączać birthday.h, a Ty w danym module polegasz na takim dołączaniu i przy którejś kompilacji będziesz miał błędy. Na szczęście to jest prosty przykład i poprawić można łatwo. Gorzej jak to będzie kobyła 1M+ kodu i potem weź szukaj brakujących include'ów...

2
Draaz napisał(a):

person.h

#pragma once

#include "birthday.h"    //dodany, ponieważ konstruktor pobiera obiekt tej klasy oraz obiekt Birthday jest polem tej klasy
#include <string>
using namespace std;    //przestrzeni nazw potrzebuje string, więc dodajemy lub bezpośrednio przed zmienna: std::string n, std::string name, ale wtedy musisz std dodać również w pliku person.cpp

class Person
{
public:
	Person(string n, Birthday b);
	void printInfo();
private:
	string name;
	Birthday bd;
};

Wrzucanie using namespace std; do pliku nagłówkowego to bardzo zła praktyka. Wszędzie gdzie wykorzystasz klasę Person będziesz też domyślnie korzystał z tej przestrzeni nazw.
To może doprowadzić do bałaganu w wykorzystanych modułach/klasach/kontenerach.
Może lepiej nie uczyć początkującego złych nawyków.

0

Dziękuję Bartosz, dziękuję YaHooo, dzięki YooSy.

Bardzo Ci dziękuję Draaz za pełny kod z wyjaśnieniem, muszę się jakoś odwdzięczyć.

Założę konto i walcie jak w dym. Mogę wstawić kody, które mam.

0

Dziękuję Ci Draaz, że dodałeś opis include'ów i przestrzenie nazw - zrobiłeś to na identycznym przykładzie jak wyżej (właśnie z uwzględnieniem domyślnych przestrzeni nazw). Solidna robota. Bardzo dziękuję za pomoc!

Ogólnie to jak by wyglądał program z użyciem #ifndef BIRTHDAY_H #define BIRTHDAY_H (...) #endif // BIRTHDAY_H w pliku birthday.h oraz #ifndef PERSON_H #define PERSON_H (...) #endif // PERSON_H zamiast #pragma once ??? Myślę, że nie tylko ja się ucieszę z odpowiedzi, gdyż ciężko o ten temat. Podobno pramga once nie jest standardem, może w ttym miejscu zacytuję treść zawartą w wikipedii odnośnie pragma once: "Zapobiega ona ponownemu załączeniu treści całego pliku, w którym została użyta. Metoda ta jednak nie ma oparcia w oficjalnym standardzie. Podobnie, jak wszystkie użycia dyrektywy #pragma, jej ewentualna obsługa jest rozszerzeniem wprowadzonym przez dany kompilator i nie jest przenośna pomiędzy różnymi narzędziami. ".

W programowaniu bardzo ważna jest przenośność, więc jakby ktoś wstawił kod z użyciem ifndef, define (...) endif z wyjaśnieniem różnic (nawet bez wyjaśnienia będzie super) będę wdzięczny.

0

To po prostu wygląda w ten sposób:

#ifndef person_h
#define person_h

#include<iostream>

class Person....

#endif

W plikach .h to dodajesz

0

I tak większość kompilatorów obsługuje pragma once z wyjątkiem jakiejś egzotyki, więc tym w ogóle bym się nie przejmował.
A te ifdefy są brzydkie.
I zupełnie nie chronią przed powtarzającą się nazwą nagłówka - chyba żeby dawać tam guida jakiegoś zamiast nazwy pliku, a to będzie jeszcze brzydsze.

0

I zupełnie nie chronią przed powtarzającą się nazwą nagłówka - chyba żeby dawać tam guida jakiegoś zamiast nazwy pliku, a to będzie jeszcze brzydsze.

Do tego preprocesor musi przejść plik w poszukiwaniu endifa nawet jeśli plik był już includowany, chyba ze masz szczęście używać kompilatora, który to optymalizuje - tak czy siak preprocesor musi to przeanalizować, więc pragma ma szanse zadziałać szybciej :)

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