Jak to powinno się robić?

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?

0

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

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.

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ć).

0

taki przykładowy kod.

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.

0

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

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

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

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

int main()
{     
	A a;
	B b;

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

A.h

#pragma once
#include <iostream>

class B;

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

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

A.cpp

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

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

B.h

#pragma once
#include <iostream>

class A;

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

B.cpp

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

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

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

0
Sopelek napisał(a):

taki przykładowy kod.

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

 
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;
}
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 ===

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.

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.

2

Jeżeli masz:

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:

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:

class B;
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.

0

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

Pozdrawiam.

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.

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