Projekt z programowania obiektowego - czy dobrze rozumiem podobiekt?

0

Witam, dostałem taki projekt z programowania obiektowego (podstawy podstaw) - > patrz załączniki. I generalnie moja wiedza z c++ sięga konstruktor-destruktor, przeciążenie funkcji, operatorów i parę innych z podstaw. Mój problem jest taki - średnio rozumiem to, co jest tutaj napisane (czyt: nie rozumiem polecenia).

No i na początku chciałbym się spytać, czy dobrze rozumiem podobiekt: niech będzie sobie klasa samochód, oraz jej obiekt np marka_Ford. To podobiektem obiektu Ford będzie jakiś model tej firmy?

Kolejna rzecz: mógłby ktoś dać przykład jakiegoś prostego podobiektu tworzonego dynamicznie? Google nic nie pokazuje, albo przynajmniej ja znaleźć nie potrafię.

2 akapit i informacja o wywołaniu konstruktora/destruktora - nie wiem o co chodzi z tym _DEBUG

i generalnie, jeśli ktoś mógłby dać jakiś szkic/zarys/schemat tego, co powinienem zrobić to byłbym bardzo wdzięczny.

0

Nic dziwnego że google nic ni pokazuje bo nie ma czegoś takiego jak podobiekt.
Jest klasa pochodna ewentualnie obiekt klasy pochodnej.

0

Z tym podobiektem to chyba chodzi o to że pole klasy ma być jako wskaźnik na inny obiekt. Ale i tak ta treść nie ma sensu, wpisałeś woltomierz a nie ma ani słowa o tym jak ten woltomierz ma wyglądać

1

Jest napisana notka o 'podobiekcie' dynamicznym, który ma być inicjowany na zero. Wyklucza to zatem klasę pochodną. Swoją drogą, obecnie powinno się używać nullptr, a nie zera.

class X {
	public:
		int jakasLiczba;
}

class Obiekt {

	public:
		Obiekt();
		X *foo; // Uzycie tutaj '= nullptr' mozliwe dopiero w C++11

};

Obiekt::Obiekt : foo(0) // nullptr w C++11
{}

2 akapit i informacja o wywołaniu konstruktora/destruktora - nie wiem o co chodzi z tym _DEBUG

Trzeba użyć dyrektywy preprocesora #ifdef.

#ifdef _DEBUG
	// Ten kod wykona sie tylko wtedy gdy _DEBUG bedzie zdefiniowane
	std::cout << "DEBUG!" << std::endl;
#endif

Żeby zdefiniować _DEBUG trzeba użyć dyrektywy #define albo wrzucić do ustawień kompilatora.

1
Dantez napisał(a):

Trzeba użyć dyrektywy preprocesora #ifdef.

#ifdef _DEBUG
	// Ten kod wykona sie tylko wtedy gdy _DEBUG bedzie zdefiniowane
	std::cout << "DEBUG!" << std::endl;
#endif

Żeby zdefiniować _DEBUG trzeba użyć dyrektywy #define albo wrzucić do ustawień kompilatora.

Takie rozwiązanie jest bezsensu jest trudne w utrzymaniu (łatwo sie pomylić przy każdym użyciu).
Lepiej zrobić tak:

#ifdef _DEBUG
    #define DEBUG_LOG(a) std::cout << __FILE__ << "(" << __LINE__ << "): " << a << std::endl
// dla gcc mozna użyc takiej wersji:
//  #define DEBUG_LOG(a) std::cout << __FILE__ << "(" << __LINE__ << ") "<< __FUNCTION__ << " : " << a << std::endl
#else
    #define DEBUG_LOG(a)
#endif

// potem użycie
Klasa::Klasa() {
     DEBUG_LOG("Konstrukcja");
     // dalsza inicjalizacja
}
0

hmm, a mogę dostać zarys tego, co powinienem zrobić? Napisze może, co wiem:
Stworzyć klasę główną woltomierz i dajmy na to 3 podklasy (wskazówka, obudowa, układ_w_środku), w tym (przykładowo) dwie tworzone dynamicznie (inicjowane na zero, bądź, jak napisał Dantez z wykorzystaniem nullptr) , jedna automatycznie. Przy wywoływaniu konstruktora i destruktora powinna się po prostu pojawiać informacja o tym, że został on wywołany (za pomocą _DEBUG) - i tu jest pytanie, bo mamy oddać projekt w Visual Studio 2013 i prowadzący powiedział, żeby coś ustawić w Właściwości projektu -> C/C++ -> Preprocessor -> Preprocessor Definitions. Aczkolwiek nie mam pojęcia, co tu należałoby zrobić w związku z tym _DEBUG. Można krótki komentarz do tego?
Idąc dalej, w klasie głównej woltomierz ma być zmienna typu static zliczająca ilość utworzonych obiektów (czyli jak mniemam to jest statyczne pole klasy) oraz "statyczna metoda zwracająca statyczne pole klasy" - co to jest statyczna metoda?

No i w sumie na tym się póki co kończą moje pytania, bo o kopiowaniu obiektów klas oraz o operatorach muszę się pouczyć. Z góry dziękuje za odpowiedzi.

1
#include <iostream>
#define _DEBUG_
using namespace std;
class foo
{
	private:
		int x;
	public:
		static short int ile;
		foo(int x0) : x(x0)
		{
			
			/*twoj kod*/
			
			
			//inkrementujesz licznik obiektow przy powolaniu do zycia//
			inc_count();
			
		}
		static int inc_count() 
		{
			++ile;
			//++x; <- To juz nie zadziala, metoda statyczna sluzy do obslugi statycznych skladowych, ponadto moze
			//tez wywolywac inne funkcje statyczne swojej klasy.
			#ifdef _DEBUG_
			cout << "Obiekt powolany do zycia... Ilosc obiektow:" << ile << "\n";
			#endif
		}
		static int ret_count()
		{
			return ile;
			
		}
		
};
short int foo::ile=0;

int main()
{
	foo x(6);
	foo z(66);
	foo y(666);
	
	
}

 

Statyczna zmienna to zmienna która występuje w ilości sztuk jeden dla wszystkich obieków tej klasy, jest tak jakby "współdzielona".
Na powyższym przykładzie, przy wywołaniu konstruktora, wywołujemy statyczną metodę inc_count() która preinkrementuje licznik obiektów i wypisuje który to już powołany do życia obiekt.
Zauważ że nie ważne dla którego obiektu klasy foo wywołasz ret_count() zwróci zawsze tą samą wartosć.

Wydaje mi się że obiekty które wymyśliłeś są nieco nietrafione.
Bo jaką funkcjonalność może oferować obudowa, oprócz trzymania wszystkiego w kupie.
Klasa główna jest troche taką obudową ponieważ w woltomierzu, i wskaźnik/lcd i uklad który mierzy są zawarte w obudowie.

0
Dantez napisał(a):

Jest napisana notka o 'podobiekcie' dynamicznym, który ma być inicjowany na zero. Wyklucza to zatem klasę pochodną. Swoją drogą, obecnie powinno się używać nullptr, a nie zera.

class X {
	public:
		int jakasLiczba;
}

class Obiekt {

	public:
		Obiekt();
		X *foo; // Uzycie tutaj '= nullptr' mozliwe dopiero w C++11

};

Obiekt::Obiekt : foo(0) // nullptr w C++11
{}

możesz powiedzieć, co oznacza inicjowanie na zero (tj. rozumiem jak, ale nie rozumiem po co)? Bo i tak potem muszę użyć

foo=new X

aby gdzieś zalokowało w pamięci ten obiekt. Bo spodziewałem się, że po użyciu nullptr wartości będą automatycznie ustawione na 0, czyli w tym kodzie by to onzaczało jakasLiczba=0

, ale chyba się myliłem.

Np. tutaj jak nie wywołam metody 
```cpp
ustaw

, to jakasLiczba

 ma zupełnie randomowe wartości:

```cpp
 #include <iostream>

class X {
public:
	int jakasLiczba;
};

class Obiekt {
public:
	Obiekt();
	X *foo = nullptr;
	void ustaw();
	void wypisz();
};

Obiekt::Obiekt()
{
	foo = new X;
}

void Obiekt::ustaw()
{
	foo->jakasLiczba = 5;
}
void Obiekt::wypisz()
{
	std::cout << foo->jakasLiczba;
}

int main()
{
	Obiekt s;
	s.ustaw();
	s.wypisz();
	std::cin.get();
	std::cin.get();
}

Istnieje jeszcze jedna możliwość, tj. być może, aby ustawiało wartość 0 to powinienem użyć tego:

Dantez napisał(a):
Obiekt::Obiekt : foo(0) // nullptr w C++11
{}

ale wyskakuje błąd kompilacji

declaration is incompatible with overloaded function "Obiekt::Obiekt" (declared at line 10) 
1

@kalwi
Po co wskaźnik na obiekt inicjalizować zerem?
Generalnie jest kilka ważnych przesłanek, Zero służy między innymi do sprawdzenia czy wskaźnik nadaje sie do użytku.
Dajmy na to że masz jakąś funkcje która operuje na wskazniku na klase.
O tak jak tu na przykladzie

#include <iostream>
class X 
{
    public:
        int jakasLiczba;
        X(int x) : jakasLiczba(x)
        {
			
		}
		int get_number()
		{
			return jakasLiczba;
		}
};
 
class Obiekt 
{
 
		X* foo;
    public:
        Obiekt() : foo(0)
        {
			
		}
		void alloc_foo(int xy) 
		{
			foo = new X(xy);
		}
		int get_x_number()
		{
			if(!(foo)) return -1;// foo niezainicjalizowane zwracam -1; bez sprawdzenia czy istnieje taki obiekt dostalbys sigsegv
			else return foo->get_number();
		}
};


int main()
{
	Obiekt xy;//tworzy nowy obiekt xy i inicjalizuje wskaznik foo zerem;
	std::cout<<xy.get_x_number();//wypisujemy składową jakasLiczba za pomocą wskaźnika na X, nie utworzylismy obiektu X wiec 
								 //wskaznik bedzie zerowy i zwróci -1;
}

W konstruktorze inicjalizujesz wskaznik zerem, jesli bedziesz przy kazdej funkcji sprawdzał czy jakimś cudem nie wskazuje on zera, to prawdopodobnie otrzymasz poprawne wyniki.
Nie zapomnij oczywiście o sprawdzaniu wskaznika w destruktorze wskaźnik zerowy mozemy sobie usuwac ile chcemy ponieważ i tak jest to bezcelowe.
Natomiast niezerowy wskaznik mozemy usuwać tylko raz, inaczej program sie wysypie.

0

hmmm, całkiem dobre info. Mam teraz taki problem z operatorem przypisania. Mianowicie:

Woltomierz.h:

 #pragma once
															/* Header klasy glownej Woltomierz.h */
#ifdef _DEBUG
#define DEBUG_LOG(a) std::cout << a << std::endl
#else
#define DEBUG_LOG(a)
#endif

#include "Uklad_Pomiarowy.h"
#include "Przetwarzanie_Wynikow.h"
#include "Wyswietlacz_LCD.h"

class Woltomierz
{
public:
	Wyswietlacz_LCD * wyswietlacz_LCD;						/* Obiekt dynamiczny klasy Wyswietlacz_LCD bedacy skladnikiem klasy Woltomierz */
	Przetwarzanie_Wynikow przetwarzanie_wynikow;			/* Obiekt automatyczny klasy Przetwarzanie_Wynikow bedacy skladnikiem klasy Woltomierz */
	Uklad_Pomiarowy uklad_pomiarowy;						/* Obiekt automatyczny klasy Uklad_Pomiarowy bedacy skladnikiem klasy Woltomierz */
	std::string nazwa;										/* Okresla nazwe obiektu typu Woltomierz*/
	static int ile_obiektow;								/* Zmienna statyczna mowiaca o ilosci obiektow typu Woltomierz */
	static int zwroc_ile();									/* Zwraca wartosc rowna ilosc "zyjacych" obiektow */
	const Wyswietlacz_LCD ZwrocWartosc();							/* Zwraca adres do obiektu dynamczinego typu Wyswietlacz_LCD */
	Woltomierz(const Woltomierz & woltomierz);					/* Konstruktor kopiujacy klasy Woltomierz */
	Woltomierz(std::string nazwa);							/* Konstruktor klasy Woltomierz */
	~Woltomierz();											/* Dekonstruktor klasy Woltomierz */
	void wypisz();											/* Wypisuje informacje o ilosci obiektow typu Woltomierz */
	Woltomierz Woltomierz::operator=( const Woltomierz & woltomierz);	/* Operator przypisania dla klasy Woltomierz */
};		

Plik Woltomierz.cpp

#include "Woltomierz.h"

int Woltomierz::ile_obiektow = 0;								/* Zmienna statyczna zawierajaca informacje o ilosci obiektow */

const Wyswietlacz_LCD Woltomierz::ZwrocWartosc()
{
	return *wyswietlacz_LCD;
}

Woltomierz::Woltomierz( const Woltomierz & woltomierz)				/* Konstruktor kopiujacy klasy Woltomierz */
{
	this->przetwarzanie_wynikow = woltomierz.przetwarzanie_wynikow;
	this->uklad_pomiarowy = woltomierz.uklad_pomiarowy;
	this->nazwa = woltomierz.nazwa;
	wyswietlacz_LCD = new Wyswietlacz_LCD;
	*wyswietlacz_LCD = const_cast<Woltomierz&>(woltomierz).ZwrocWartosc();
	DEBUG_LOG("Konstruktor kopiujacy klasy Woltomierz uruchomiony!");
	ile_obiektow++;
}

int Woltomierz::zwroc_ile()
{
	return ile_obiektow;
}

Woltomierz::Woltomierz(std::string nazwa)						/* Definicja konstruktora klasy Woltomierz */
{
	wyswietlacz_LCD = new Wyswietlacz_LCD();					/* Alokuje pamiec dla podobiektu dynamicznego wyswietlacz_LCD */
	this->nazwa = nazwa;
	DEBUG_LOG("Konstruktor klasy Woltomierz uruchomiony!");
	ile_obiektow++;
}

Woltomierz::~Woltomierz()										/* Definicja destruktora klasy Woltomierz */
{
	delete wyswietlacz_LCD;
	DEBUG_LOG("Destruktor klasy Woltomierz uruchomiony!");
	ile_obiektow--;
}

Woltomierz Woltomierz::operator=(const Woltomierz & woltomierz)	/* Definicja operatora przypisania dla klasy Woltomierz*/
{
	przetwarzanie_wynikow = woltomierz.przetwarzanie_wynikow;
	uklad_pomiarowy = woltomierz.uklad_pomiarowy;
	nazwa = woltomierz.nazwa;
	wyswietlacz_LCD = new Wyswietlacz_LCD;
	*wyswietlacz_LCD = const_cast<Woltomierz&>(woltomierz).ZwrocWartosc();
	return *this;
}

I generalnie wszystko tu działa, tak jak powinno, ale chciałbym zrobić operator = (oraz analogicznie konstruktor kopiujący) bez rzutowania typu

const_cast

. Lecz jak tak zrobię, to wtedy muszę dać

 Woltomierz Woltomierz::operator=(Woltomierz & woltomierz)	/* Definicja operatora przypisania dla klasy Woltomierz*/

czyli bez const. Nie udaje mi się rozwiązać tego problemu, a jak mniemam leży on w kopii głębokiej obiektu dynamicznego wyswietlacz_LCD. Mogę prosić o jakąś wskazówkę?

1

Jeśli chcesz użyć funkcji operatorowej na rzecz obiektu stałego, powinno to wygladać tak:

Wyswietlacz_LCD Woltomierz::ZwrocWartosc() const

dodanie znacznika <code class="cpp"> - Furious Programming

0

Eh, kolejny problem :/ I bynajmniej jest to dość dziwny błąd. Mianowicie, jak stworzę zmienną globalną typu Woltomierz (moja klasa główna) z parametrem("nazwa") i zamknę program w trakcie działania (tj. wszystko się wykona i program oczekuje na ostatni enter przed zakończeniem działania i wtedy zamknę konsolę) to gdy nie ma trybu debugowania włączenia po prostu wywala (wywala= program nieoczekiwanie zakończył działanie..) aplikację. A z włączonym debugowaniem wyświetla: Unhandled exception at 0x5C0E04A9 (msvcp120d.dll) in Projekt 1 - Woltomierz.exe: 0xC0000005: Access violation reading location 0xFEEEFEEE.

No i dla formalności plik z mainem tak wygląda:

 #include "Woltomierz.h"

Woltomierz globalny("globalny");

int _tmain(int argc, _TCHAR * argv[])
{
	std::cin.get();
	std::cin.get();
}

Co ciekawe, gdy nie ma zmiennej globalnej, a zrobię to samo jak jest tylko zmienna lokalna to problemu nie ma. Z kolei dojście do końca programu (tj. bez zamykania konsoli) nie wywołuje żadnego erroru.

Pliki .h i .cpp wkleiłem w moim poprzednim poście (zmieniłem jedynie to, co Proxima zalecił), więc nie ma sensu tego powtarzać. Z rzeczy jeszcze, które mogą być pomocne, to jak ustawię breakpointa na

 Woltomierz globalny("globalny");

oraz na konstruktor dla klasy Woltomierz

Woltomierz::Woltomierz(std::string nazwa)																
{		
	wyswietlacz_LCD = new Wyswietlacz_LCD();															/* Alokuje pamiec dla podobiektu dynamicznego wyswietlacz_LCD */
	this->nazwa = nazwa;	
	DEBUG_LOG("Konstruktor klasy Woltomierz uruchomiony!");
	ile_obiektow++;
}

to po przejściu przez cały konstruktor wraca do

 Woltomierz globalny("globalny");

a następny step to jest wejście do takiej pętli wgłąb jakiegoś pliku o nazwie crt0dat.c :

     while ( pfbegin < pfend )
        {
            /*
             * if current table entry is non-NULL, call thru it.
             */
            if ( *pfbegin != NULL )
                (**pfbegin)();
            ++pfbegin;
        }
}

i następnie przechodzi przez to ileś razy (aczkolwiek nie wiem cóż to oznacza). Cóż źle zrobiłem? (jeśli jakiś plik jeszcze będzie potrzebny, to wkleję).

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