Jak to powinno się robić?

Jak to powinno się robić?
Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
0

Witam.

Mam pytanie odnoszące się do OOP, które ciężko było mi sformułować w temacie, więc nazwa jej taka jaka jest.

Aktualnie, gdy potrzebuję w metodzie klasy A danych z obiektu klasy B, to przekazuje ten obiekt przez referencję jako argument metody.
Jednak robiąc to w taki sposób mogę natrafić na problem. (W zasadzie to właśnie na niego natrafiłem)
Mianowicie. W klasie A będzie metoda przyjmująca przez referencję obiekt klasy B, a w klasie B z kolei będzie metoda przyjmująca przez referencję obiekt klasy A. Taki kod się nie kompiluje, wiadomo chyba dlaczego.
I tu nasuwa się moje pytanie z tematu. Jak w obiekcie powinno się korzystać z danych innego obiektu? Jak powinno się rozwiązać taki problem?

edytowany 2x, ostatnio: Sopelek
hauleth
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:6 dni
0

Ja bym zrobił osobną klasę, która by przechowywała dane uwspólnione.


43
Interfejs chyba nie służy przechowywaniu danych...
0

w klasie B pola prywatne , tworzysz publiczne gettery settery, do metody klasy A przekazujesz obiekt klasy B i w niej poprzez gettery pobierasz wartosci, a settery ustawiasz.

byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 7 lat
1
Sopelek napisał(a):

W klasie A będzie metoda przyjmująca przez referencję obiekt klasy B, a w klasie B z kolei będzie metoda przyjmująca przez referencję obiekt klasy A. Taki kod się nie kompiluje, wiadomo chyba dlaczego.

Pokaż kod, bo wcale nie jest jasne dlaczego się nie kompiluje. Nic nie stoi na przeszkodzie, żeby coś takiego działało.

Sopelek napisał(a):

I tu nasuwa się moje pytanie z tematu. Jak w obiekcie powinno się korzystać z danych innego obiektu? Jak powinno się rozwiązać taki problem?

Taki problem rozwiązuje się różnie. Zależy od tego czy obiekt klasy A ma być na stałe powiązany z obiektem klasy B, czy tylko w tym jednym momencie. Jak w jednym momencie to normalnie można przekazać referencję/wskaźnik do metody. W drugim przypadku taką zależność można wstrzyknąć przez konstruktor, lub inną metodę i trzymać w klasie referencję/wskaźnik do tego obiektu. Nazywa się wstrzykiwaniem zależności, dependency injection(tak żebyś wiedział czego w google szukać).


Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
0

taki przykładowy kod.

Kopiuj
class A
{
    public:
    /* jakis kod ... */
    void MetodaA(B& b)
    {

    }
};
class B
{
    public:
    /* jakis kod ... */
    void MetodaB(A& a)
    {

    }
};
int main()
{
    return 0;
}

konsola:
.... main.cpp|5|error: 'B' has not been declared|

W drugim przypadku taką zależność można wstrzyknąć przez konstruktor, lub inną metodę i trzymać w klasie referencję/wskaźnik do tego obiektu. Nazywa się wstrzykiwaniem zależności, dependency injection(tak żebyś wiedział czego w google szukać).

dzięki, to mi się przyda ;], ale niestety dalej nie rozwiąże tego problemu z kodu powyżej.

edytowany 4x, ostatnio: Sopelek
0

<quote="849482">taki przykładowy kod.

Kopiuj
class B;
class A
{
    public:
    /* jakis kod ... */
    void MetodaA(B& b)
    {

    }
};
class B
{
    public:
    /* jakis kod ... */
    void MetodaB(A& a)
    {

    }
};
int main()
{
    return 0;
}

sproboj teraz

byku_guzio
Jeżeli w klasie A będzie używał metod klasy B to nie rozwiąże to problemu, bo w tym momencie jest już potrzebna definicja klasy B.
Sopelek
@lkj. tak już próbowałem, jednak kompilator musi znać jeszcze rozmiar klasy B.
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 7 lat
1

Tu masz rozwiązanie tego problemu, ale jest jedno ale :p Posiadanie takich wzajemnych zależności najprawdopodobniej świadczy o złym projekcie aplikacji i pewnie trzeba jeszcze raz przemyśleć te klasy.

main.cpp

Kopiuj
#include "A.h"
#include "B.h"

int main()
{     
	A a;
	B b;

	a.metodaA(b);
	b.metodaB(a);
	return 0;
}

A.h

Kopiuj
#pragma once
#include <iostream>

class B;

class A
{
public:
	void metodaA(B &b);

	void fooA()
	{
		std::cout << "Foo A" << std::endl;
	}
};

A.cpp

Kopiuj
#include "A.h"
#include "B.h"

void A::metodaA(B &b)
{
	b.fooB();
}

B.h

Kopiuj
#pragma once
#include <iostream>

class A;

class B
{
public:
	void metodaB(A &a);
	void fooB()
	{
		std::cout << "Foo B" << std::endl;
	}
};

B.cpp

Kopiuj
#include "B.h"
#include "A.h"

void B::metodaB(A &a)
{
	a.fooA();
}

//działa na 100% - sprawdzone ;)


edytowany 1x, ostatnio: byku_guzio
CI
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 10 lat
0
Sopelek napisał(a):

taki przykładowy kod.

Kopiuj
class A
{
    public:
    /* jakis kod ... */
    void MetodaA(B& b)
    {

    }
};
class B
{
    public:
    /* jakis kod ... */
    void MetodaB(A& a)
    {

    }
};
int main()
{
    return 0;
}

konsola:
.... main.cpp|5|error: 'B' has not been declared|

Można też przed definincjami klas podać same deklaracje

Kopiuj
 
class A;
class B;

class A
{
    public:
    /* jakis kod ... */
    void MetodaA(B& b)
    {

    }
};
class B
{
    public:
    /* jakis kod ... */
    void MetodaB(A& a)
    {

    }
};
int main()
{
    return 0;
}

edytowany 1x, ostatnio: cichociemny
Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
0

@byku_guzio
niestety dalej nie trybi

obj\Debug\main.o||In function main':| mietnik\test\main.cpp|9|undefined reference to A::metodaA(B&)'|
mietnik\test\main.cpp|10|undefined reference to `B::metodaB(A&)'|
== Build finished: 2 errors, 0 warnings ===

byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 7 lat
0

Jeżeli to jest gcc i masz pliki dokładnie takie jak ja to po prostu źle kompilujesz. Nie mam tu w tym momencie gcc jeszcze, więc nie sprawdzę, ale polecenie powinno wyglądać mniej więcej tak: g++ A.cpp B.cpp main.cpp -o output

Jeżeli korzystasz z jakiegoś IDE, to te wszystkie pliki muszą być dodane do projektu.


edytowany 1x, ostatnio: byku_guzio
Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
0

Są wszystkie w jednym projekcie. Nawet podeślę screeny jak to wygląda
http://screenshooter.net/6090290/qqdusmh //main.cpp
http://screenshooter.net/6090290/jyyylqj //B.h
http://screenshooter.net/6090290/selmlct //A.cpp
http://screenshooter.net/6090290/ddniojn //A.h
http://screenshooter.net/6090290/jashlgs //B.cpp
http://screenshooter.net/6090290/mfhilgx //wygląd projektu

Ale jednak skorzystam z Twojej rady i postaram się omijać takie sytuacje.

byku_guzio
Widzę, że to jest code::blocks, będę to dzisiaj instalował to popatrzę jak to tam wygląda, ale pewnie dopiero po 12
Sopelek
ok, dzięki wielkie za fatygę
_13th_Dragon
  • Rejestracja:prawie 20 lat
  • Ostatnio:16 dni
2

Jeżeli masz:

Kopiuj
struct A
  {
   int a;
   void foo(B b) { b.b=0; } // odwołanie się do składowej B (b.b), przed tym miejscem już nie wystarczy class B;
  };

struct B
  {
   int a;
   void foo(A a) { a.a=0; } // odwołanie się do składowej A (a.a), przed tym miejscem już nie wystarczy class A;
  };

To nie ma żadnego sposobu aby to się skompilowało cokolwiek byś nie robił.

Natomiast wystarczy to przerobić na:

Kopiuj
struct A
  {
   int a;
   void foo(B b); // przed tym wystarczy class B;
  };

struct B
  {
   int a;
   void foo(A a); // przed tym wystarczy class A;
  };

void A::foo(B b) { b.b=0; }
void B::foo(A a) { a.a=0; }

I da się to łatwo pogodzić dodając z przodu wiersz:

Kopiuj
class B;

Wykonuję programy na zamówienie, pisać na Priv.
Asm/C/C++/Pascal/Delphi/Java/C#/PHP/JS oraz inne języki.
edytowany 2x, ostatnio: _13th_Dragon
_13th_Dragon
EDIT: dodano komentarze w kodzie.
byku_guzio
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 7 lat
1
Sopelek napisał(a):

Są wszystkie w jednym projekcie. Nawet podeślę screeny jak to wygląda
http://screenshooter.net/6090290/qqdusmh //main.cpp
http://screenshooter.net/6090290/jyyylqj //B.h
http://screenshooter.net/6090290/selmlct //A.cpp
http://screenshooter.net/6090290/ddniojn //A.h
http://screenshooter.net/6090290/jashlgs //B.cpp
http://screenshooter.net/6090290/mfhilgx //wygląd projektu

Ale jednak skorzystam z Twojej rady i postaram się omijać takie sytuacje.

Dobra już wiem ocb, bo za pierwszym razem też mi tak się posypało. Przy tworzeniu pliku nie zaznaczyłeś dla jakich buildów ma być brany pod uwagę. Dotyczy to plików cpp, nie h.
Teraz prawym na A.cpp > Properties... > Build > Belongs to targets - zaznacz wszystkie(zarówno debug jak i release). Po tym powinno śmigać.

_13th_Dragon też ma rację, ale ja osobiście wolę dzielić klasy na pliki. Po pewnym czasie one już takie małe nie są i jak wszystko jest napakowane w jednym miejscu to się człowiek gubi.


edytowany 1x, ostatnio: byku_guzio
_13th_Dragon
@byku_guzio, a co ma to co napisałem do dzielenia na pliki? byku_guzio ma racje co do dzielenia klas na pliki, ale ja osobiście wole spędzać wakacje w górach na nartach. ;P
byku_guzio
no napisałeś jak to zrobić w jednym pliku ;) ehhh... też bym wolał, ale trzeba w robocie siedzieć :p
_13th_Dragon
No to nie zrozumiałeś o czym mowa, ok dodam komentarz do tamtego postu. Tak a propos jeżeli klasy A i B są tak mocno zależne od siebie nawzajem to w 70% to wynik niepoprawnego projektowania, prawdopodobnie potrzebne jest class A { public: class B { /* tu składowe B / }; / tu składowe A */ }; więc najwyżej dwa pliki ab.h i ab.cpp, w pozostałych 30% też bym nie dzielił tego na cztery pliki co najwyżej na trzy ab.h, a.cpp i b.cpp ale w tym przypadku podział na a.cpp i b.cpp jest strasznie sztuczny. Uwaga, dotyczy tylko klas zależnych od siebie nawzajem.
Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
0

Dzięki wszystkim za sugestie i wskazówki, postaram się lepiej rozplanowywać klasy, żeby nie mącić tak w kodzie.

Pozdrawiam.

edytowany 1x, ostatnio: Sopelek
43
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 6 lat
  • Postów:61
1

Dorzucę, że nie zgodzę się z tym, że jest to błąd projektowania, ani że to jest jakieś "mącenie".

http://en.wikipedia.org/wiki/Coupling_(computer_science)#Types_of_coupling
http://stackoverflow.com/questions/1897537/why-are-circular-dependencies-considered-harmful

Nie rozumiem dlaczego zostało stwierdzone, że rozwiązanie błędne, na przykładzie klas A i B...
To jest bogaty i trudny temat, nie sądzę żeby żadne generalizacje miały tutaj zastosowanie.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.