Jak pobrać obiekt, na którym wywoływana jest metoda.

0

Cześć.

Mam taką metodę

string Number::subtraction(Number* number)
{
	string result = "";
	alignNumbers(number);
	Number biggerNumber;
	Number smallerNumber;

	if (this->intPart[0] > number->intPart[0])
	{
		biggerNumber = this; // Tu nie bardzo wiem jak
	}

	return "";
}

Chciałbym, żeby do biggerNumber zapisywał mi się obiekt, na którym wywołuję metodę, jeśli porównanie jest prawdziwe.

1
biggerNumber = *this;
0

Nie sądziłem, że to potrzebne.

#pragma once
#include <string>

using namespace std;

class Number
{

private:

	const string INT_PART = "INT";
	const string DOUBLE_PART = "DOUBLE";
	const char PLUS = 43;
	const char MINUS = 45;

	string number;
	string intPart;
	string doublePart;
	char signNumber;

	void assignSign();
	void removeSing();
	void splitNumber();

	void alignNumbers(Number *number);
	void addZeroAfter(int howMany, string & where);
	void addZeroBefore(int howMany, string & where);

public:
	Number(string number);
	Number();
	string addition(Number *number);
	string subtraction(Number *number);
	~Number();
};

1

Parę spraw odnośnie powyższego:
Po pragmie poznaję, że to plik nagłówkowy, więc wywal to using namespace std; z globalnego zasięgu. Niedobra to praktyka.
const char PLUS = 43; - co to za magiczne 43? I po co taka osobna stała każdemu obiektowi potrzebna? Może chciałeś tam stałą statyczną mieć?
string addition(Number *number); dlaczego bierzesz argument "przez wskaźnik" tutaj? Czy number jest opcjonalny, w sensie, że jest szansa, że poślesz tam nullptr? Wtedy wskaźnik miałby jakieś uzasadnienie. Jeżeli nie - to nie czyściej byłoby przez referencję?
Co właściwie ma robić ta funkcja? Działać jak operator+=? To może czyściej by było po prostu przeładować/przeciążyć ten operator? Czytelniejszy kod by z tego wyszeł. Ale jeśli już funkcja, to może "add" zamiast "addition"? No i ten string jako typ zwrotny... Jak dziecko niespodzianka, chyba czeka, żeby wiedźmin po niego przyszedł, bo po co innego tam jest to nie wiem. :P

Zastanów się jak natywne typy numeryczne działają. Twój typ powinien działać podobnie, żeby jak najmniej zaskakiwać użytkownika. Jak chcesz zwrócić jego reprezentację jako string - dodaj sobie jakąś osobną metodę std::string to_string() const.

Odnośnie samego problemu - chcesz sobie wybrać, która z liczb jest większa, a która mniejsza, żeby ułatwić sobie odejmowanie, prawda? Jeśli już w ten sposób chcesz to zrobić, to może zrób sobie funkcję Number& min(Number& left, Number& right), która zwróci referencję do mniejszej z liczb i tą referencją zainicjalizuj u siebie obiekt - lub, taniej - referencję do obiektu: Number& lesser = min(a, b);. Analogicznie z max. Następnie działaj z tymi referencjami jak Ci wygodnie.
W ten sposób nie potrzebujesz nawet konstruktora kopiującego...

No ale, właśnie - każdy użytkownik takiej klasy spodziewałby się, że konstruktor kopiujący istnieje! Więc i tak dobrze by było go zaimplementować.

0

Normalnie programuje w Javie. Tak jest to plik nagłówkowy. Nie bardzo ogarniam jeszcze zasady C++. Czemu nie powinno tam być using namespace std? Tak miała to być zmienna statyczna. Nie wiem czemu przez wskaźnik :D Myślałem, że tak jest dobrze. W zadaniu mam określone, żeby nie przeciążać operatorów, ale teoretycznie ma działać jak +=, ale nie do końca. Bo zależnie od znaku przy liczbach będę je przestawiał i ułatwiał sobie działanie artmetyczne. Co do zwracanego typu chcę na końcu zwrócić wynik działania. Dlatego na razie jest tak, żeby nie pluło mi błędami.

Ogólnie liczba ma być dowolnej precyzji stąd wydaje mi się, że string jest najlepszym rozwiązaniem. Dzięki za wszystkie rady.

0

To min, max jednak nie rozwiązuje mojego problemu, ponieważ moja metoda nie powinna być statyczna lub dwuargumentowa. Ogólnie propozycja min i max i tak mi się podoba, ale chciał bym ją wykonać na obiekcie i poprzez argument podać drugi obiekt. Czyli i tak muszę pobrać pierwszy obiekt do porównania jak "this". Teraz moje pytanie czy mogę to zrobić podobnie jak pisałeś? Poprzez referencję? Jeśli tak to mniej więcej jak powinno to wyglądać?

Jak coś na razie poradziłem sobie z pomocą konstruktora kopiującego.

0

Nie bardzo ogarniam jeszcze zasady C++.
To normalne. C++ to spory język. Wszystko z czasem.

Czemu nie powinno tam być using namespace std?
Bo zmuszasz użytkownika Twojego nagłówka do wciągnięcia wszystkich nazw z std do globalnej przestrzeni nazw. Ba, użytkownik nawet o tym nie wie. To może prowadzić do kolizji nazw.
Jeśli nie chcesz ciągle pisać std::cout - użyj using std::cout; w lokalnym zasięgu (np. w funkcji). Raczej nie w globalnym. ;)

Tak miała to być zmienna statyczna.
No to static się kłania, no i definicja tejże zmiennej pod definicją klasy - static T C::variable = init gdzie T to typ, C to nazwa klasy, a init to jakaś wartość inicjująca - tej może nie być, wtedy zmienna statyczna jest inicjalizowana zgodnie z regułami: http://en.cppreference.com/w/cpp/language/initialization#Non-local_variables

Nie wiem czemu przez wskaźnik :D Myślałem, że tak jest dobrze.
Czyli pewnie powinno być przez referencję. ;) Wskaźniki w nowoczesnym C++ nie są aż tak znowu często potrzebne. "Sprytne" wskaźniki (smart pointers), kontenery, owszem - ale gołe wskaźniki... Zawsze gdy takiego potrzebujesz zastanów się czy ma to uzasadnienie. (Będąc początkującym - jak teraz jesteś - nie będzie to takie znowu łatwe.) W każdym razie - gdy używasz w kodzie delete - to znaczy, że prawdopodobnie powinieneś użyć sprytnego wskaźnika, albo kontenera. A może nawet std::reference_wrapper.
Nie oznacza to, że wskaźników koniecznie powinno się bezmyślnie unikać - ale nie powinno się też ich bezmyślnie używać. ;)

W zadaniu mam określone, żeby nie przeciążać operatorów, ale teoretycznie ma działać jak +=, ale nie do końca. Bo zależnie od znaku przy liczbach będę je przestawiał i ułatwiał sobie działanie artmetyczne.
A to w += nie mógłbyś sobie ułatwiać działania? ;) W końcu chodzi o to, żeby po wszystkim było prawidłowe rozwiązanie - jak do niego dojdziesz to już detal implementacji. :P

Co do zwracanego typu chcę na końcu zwrócić wynik działania.
To jest jasne, ale dlaczego typ zwracany to std::string zamiast Number&?

To min, max jednak nie rozwiązuje mojego problemu, ponieważ moja metoda nie powinna być statyczna lub dwuargumentowa.
A co to za problem? :P

Number& Number::subtract(const Number& rhs) { // jest przecież jednoargumentowa
  const Number& lesser = min(rhs, *this);
  const Number& greater = max(rhs, *this);
  //...stuff
  return *this;
}

Tak teraz popijając sobie portera - to możesz to zrobić bez konstruktora kopiującego, ale z nim będzie łatwiej i nie będzie trzeba dublować kodu. ;)

0

Teoretycznie mamy używać standartu C++ 98. Więc nie wiem czy taki nowoczesny :D Ogólnie pewnie ja się do tego nie stosuję, ale jakoś tego za bardzo nie sprawdzają :P Co do tego zadania rozumiem, że tu użycie wskaźników w taki sposób jak ja używam nie niesie ze sobą korzyści, ale tak było mi tak łatwiej.

Nigdy się nie zagłębiałem dokładnie jak działają skrócone operatory czy jak one się tam nazywają np. +=, ale wydaje mi się, żeby w takim działaniu były przestawiane liczby do łatwiejszego dodawania, a u mnie się tak dzieje. Oczywiście samo wyklucza się przez się bo dodawanie liczb typów znakowych nie jako wymusza to na mnie (No nie wymusza, ale tak jest łatwiej :P)

Masz racje nie powinienem zwracać stringa, ale jest to też takie moje lenistwo, mogę bez pisania metody toString odczytać mój nr :P

Co do min i max to nie do końca Cię rozumiem. Ten przykład, który przedstawiłeś będzie potrzebował tego konstruktora kopiującego czy nie? Bo jednak korzystasz tam, ze wskaźnika.

Reasumując przedmiot nazywa się Zaawansowane metody programowania obiektowego (Co ciekawe najpierw nauczyli nas Javy, a teraz uczą C++) i podejście takiej jak ja tu reprezentuję chodzi mi konkretnie o ten min i max chyba nie jest do końca obiektowe. Bo jest to trochę równoznaczne metodzie statycznej, która właśnie ma dwa argumenty, które np. dodaje. Dlatego mam też w poleceniu napisane, że nie mogę korzystać z metod statycznych oraz więcej jak jednoargumentowych. Choć co do drugiego trochę się nie zgadzam. Bo równie dobrze mógłbym w obiekcie na którym działam zapisywać wynik działania metody dwuargumentowej i wyglądało by to mniej więcej jak operacje arytmetyczne na typach opakowujących w Javie.

0

Teoretycznie mamy używać standartu C++ 98.
O matko i córko, [current year meme]. :P Tylko nie mów, że zalecali Turbo C++ jako kompilator. :P Co my, kruca fuks, w Indiach jesteśmy? ;)

Masz racje nie powinienem zwracać stringa, ale jest to też takie moje lenistwo, mogę bez pisania metody toString odczytać mój nr :P

Tak, ale to kompletnie niepraktyczne. Nagle ni z tego ni z owego zmieniasz typ - co wyklucza kaskadowe wywołania metod (a.add(b).add(c).add(d); ). Jeśli nie chcesz pisać .to_string() możesz sobie przeładować std::ostream& operator<<(std::ostream&, const Number&) i tam wrzucić wynik to_string() na strumień. Potem będziesz mógł pisać std::cout << a; i wszystko będzie cacy. ;)

A, no ale to jest przeładowanie operatora... Co za idiotyczne wymagania, przecież to część C++98. ;)

Nawet bez tego - to po prostu nie ma za bardzo sensu, żebyś stringa tam zwracał.

Co do min i max to nie do końca Cię rozumiem. Ten przykład, który przedstawiłeś będzie potrzebował tego konstruktora kopiującego czy nie? Bo jednak korzystasz tam, ze wskaźnika.
Jakiego wskaźnika? Bo patrzę, patrzę, a nie widzę. ;) min i max biorą referencje (do const T) i zwracają referencje (do const T). Mógłbyś to samo zrobić ze wskaźnikami, ale tutaj ich nie ma.
Jak już masz refkę do mniejszej i większej wartości - to pracujesz sobie na ich wewnętrznych stringach, zależnie jak to robisz, możesz potrzebować zrobić kopie wewnętrznych stringów jednej z wartości, albo w ogóle kopię całego obiektu za pomocą konstruktora kopiującego (dlatego pisałem, możesz i tak i tak). Po całej matematycznej magii odejmowania sprawdzasz czy *this to była mniejsza czy większa wartość i zmieniasz odpowiednio znak wyniku (jeśli to wymagane, oczywiście). W przypadku używania kopii obiektu masz od tego funkcję, jak masz tylko kopie wewnętrznych "flaków" to robisz to ręcznie. Na koniec operatorem przypisania, albo metodą assign() zmieniasz wartość *this (*this = result; - nawet o std::move można by się pokusić) albo zamieniasz flaki *this z flakami z wynikiem, no i zwracasz *this, tak jakby to += zrobiło.
Powiedziałbym, że jednak wygodniej będzie jak sobie zrobisz konstruktor kopiujący i operator przypisania (albo ekwiwalentną metodę).

Bo jest to trochę równoznaczne metodzie statycznej,
min i max w tym przykładzie to wolne funkcje. Równie dobrze możesz zdefiniować operator<, albo metodę bool is_lesser_than(const Number&) czy jak ją tam wygodnie nazwać (wygodnie to chyba jednak operatorem, który ubiera to znaczenie w jeden rozpoznawalny znak :P) i porównać sobie nim obiekty.

Number& lesser = *this < rhs ? *this : rhs;
Number& greater = *this > rhs ? *this : rhs;

Może się zastanawiasz "zaraz, zaraz, a co jeśli są równe?" - no, ten przypadek powinieneś wyłapać na samym początku funkcji i odpowiednio zareagować. ;)

Nie widzę tu jakiegoś gwałcenia obiektowości.

0

Ok teraz rozumiem. Bo przy this użyłeś operatora * i stąd wziął się mój zły tok rozumowania, ale nie było tam nigdzie przypisania więc jest ok. Teraz tylko zmagam się jeszcze z przeciążeniem operatora << no wyświetlania obiektów. Mam coś takiego

inline std::ostream & operator << (std::ostream & stream, const Number & number)
{
	return stream << number.signNumber + number.intPart + "," + number.doublePart;
}

Teraz gdy chce wyświetlić mój numer to robię standardowo

cout << *number1 << "\n";

i jest wszystko pięknie. Jednak gdy chcę wyświetlić numer zwracany przez funkcję (Przerobiłem, żeby zwracała numer) to robię to tak

cout << number1->subtraction(number2) << "\n";

ale nie wiem czemu nie wyświetla mi się poprawnie znak. Coś takiego dostaję na output
1cba32c80f.png
Tak wygląda mój cały main:

Number *number1 = new Number("-12,533");
Number *number2 = new Number("3,6444");
cout << number1->subtraction(number2) << "\n";
cout << number1->signNumber << "\n";
cout << *number1 << "\n";

Jak by co to wiem, że jest zły wynik :P

0

Musisz pokazać więcej kodu, bo jeśli przerobiłbyś metodę subtraction ("odejmowanie" - dlaczego nie subtract, czyli "odejmij"?) w taki sposób w jaki powiedziałem, to posłałbyś na strumień ten sam wynik w obydwu przypadkach.

(Po co Ci to new tam? Number n{"42"} czy tam Number n("42") nieładne? :P)

0

Masz rację nazewnictwo nazw trochę kuleje. Zauważ jednak, że gdy pobieram sam znak z obiektu to wyświetla się poprawnie. Już trochę pozmieniałem i kod nie jest podobny do tego co wyżej napisałem, ale podam tą zmienioną wersję. Jak coś to mam metodę odejmowanie, a w niej wywołuję odejmowanieFun. Wiem, że można by to zrobić bardziej elegancko :P A no i moje rozwiązanie różni się od tego co mi zaproponowałeś.

Number Number::subtraction(Number* number)
{
	
	alignNumbers(number);
	Number biggerNumber;
	Number smallerNumber;

	if (this->intPart[0] > number->intPart[0])
	{
		biggerNumber = *this;
		smallerNumber = *number;
	}
	else
	{
		biggerNumber = *number;
		smallerNumber = *this;
	}

	if (biggerNumber.signNumber == PLUS && smallerNumber.signNumber == MINUS) // + - = + // dodawanie
	{
		biggerNumber.signNumber = PLUS;
		additionFun(biggerNumber, smallerNumber);
	}
	if (biggerNumber.signNumber == MINUS && smallerNumber.signNumber == PLUS) // - + = - // dodawanie
	{
		biggerNumber.signNumber = MINUS;
		additionFun(biggerNumber, smallerNumber);
	}
	if (biggerNumber.signNumber == PLUS && smallerNumber.signNumber == PLUS) // + + = + // Odejmowanie
	{
		biggerNumber.signNumber = PLUS;
		subtractionFun(biggerNumber, smallerNumber);
	}
	if (biggerNumber.signNumber == MINUS && smallerNumber.signNumber == MINUS) // - - = - // Odejmowanie
	{
		biggerNumber.signNumber = MINUS;
		subtractionFun(biggerNumber, smallerNumber);
	}
	removeAllZerosBefore(biggerNumber);
	return biggerNumber;
}

void Number::subtractionFun(Number & biggerNumber, Number & smallerNumber)
{
	for (int i = 0; i < biggerNumber.intPart.length(); i++)
	{
		if ((biggerNumber.intPart[i] - smallerNumber.intPart[i]) < 0)
		{
			biggerNumber.intPart[i - 1] -= char(1);
			biggerNumber.intPart[i] = (48 + 10) - smallerNumber.intPart[i] + biggerNumber.intPart[i];
		}
		else
		{
			biggerNumber.intPart[i] = biggerNumber.intPart[i] - smallerNumber.intPart[i] + 48;
		}
	}
	for (int i = 0; i < biggerNumber.doublePart.length(); i++)
	{
		if ((biggerNumber.doublePart[i] - smallerNumber.doublePart[i]) < 0)
		{
			if (i != 0)
			{
				biggerNumber.doublePart[i - 1] -= char(1);
			}
			else if (i != 0 && biggerNumber.intPart[0] != 48)
			{
				takeFromIntPart(biggerNumber);
			}
			biggerNumber.doublePart[i] = (48 + 10) - smallerNumber.doublePart[i] + biggerNumber.doublePart[i];
		}
		else
		{
			biggerNumber.doublePart[i] = biggerNumber.doublePart[i] - smallerNumber.doublePart[i] + 48;
		}
	}
}
0

Znowu puszczasz przez wskaźnik - kompletnie niepotrzebnie. A co będzie jeśli poślę tam nullptr jako argument? ;) Na upartego idzie, no. ;)

    Number biggerNumber;
    Number smallerNumber;
 
    if (this->intPart[0] > number->intPart[0])
    {
        biggerNumber = *this;
        smallerNumber = *number;
    }
    else
    {
        biggerNumber = *number;
        smallerNumber = *this;
    }

Nie używając referencji tylko kopii sprawiasz, że dodawanie jest niepotrzebnie wolne. Jasne, może na razie zrób, żeby w ogóle działało - ale wyobraź sobie, że masz licznę na kilkanaście tysięcy znaków i więcej - wtedy każda kopia staje się kosztowna i pod względem pamięci i czasu wykonania.

if (this->intPart[0] > number->intPart[0]) - czy to porównuje tylko pierwszy znak? Czyli "2555" jest mniejsze od "9"?

return biggerNumber; - Dlaczego? Co to daje użytkownikowi, poza tym, że go kompletnie zaskakuje? Dlaczego nie Number& i return *this - czyli zachowanie, które oszczędza niepotrzebnej kopii i jednocześnie nie zaskakuje?

0

No tak z tą kopią masz całkowicie rację :D, ale szczerzę to już trochę mi obrzydło to zadanie :D Bo pisałem je od nowa już ze 4 razy bo co chwilę uczę się czegoś nowego... Porównuję tylko pierwszy znak bo tam mam metodę alignNumbers(number), która wyrównuje mi liczby zerami np. jeśli mam liczby 1234 i 4 to później mam 1234 i 0004. Wydaje mi się to sporym ułatwieniem w późniejszych działaniach. Oczywiście sytuacja będzie podobna jak ta z kopią, że jest to dość nie opłacalne :D Ogólnie dodawanie i odejmowanie mi już działa. Tylko nie wiem czemu mi się ten znak taki dziwny wyświetla. Myślę, że to może być jakiś problem konkatenacji bo liczbę reprezentuję jako string, a znak jako char, ale tego nie wiem. Takie są moje przypuszczenie. Bo wyświetlenie samodzielnie samego znaku działa poprawnie.

0

Piszę od nowa ze wszystkim twoimi zaleceniami :D

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