Try catch, throw w konstruktorze

0

W jaki sposób mógłbym użyć try catch jeżeli chcę aby w klasie Triangle sprawdzane były odległości między punktami i ew. został zwrócony wyjątek.

Jak na razie zrobiłem coś takiego:
main:

try
	{
		Triangle t1(A, B, C);
	}
	catch (string w)
	{
		cout << "Wyjatek: " << w;
	}

konstruktor Triangle:

Triangle::Triangle(Point p1, Point p2, Point p3) : a(p1), b(p2), c(p3)
{
	string wyjatek = "jakis blad";
	if (a.x = 2)
	{
		throw wyjatek;
	}
}

W mainie wywołuję również kilka metod klasy Triangle i z powodu, że sprawdzanie jest zamknięte w bloku nie znajduje mi tego obiektu. Jest jakiś sposób żeby sprawdzać ten wyjątek a jednocześnie móc używać tych metod w mainie? Dodam, że pierwszy raz używam obsługi wyjątków try ,catch.

0

Jest jakiś sposób żeby sprawdzać ten wyjątek a jednocześnie móc używać tych metod w mainie? Dodam, że pierwszy raz używam obsługi wyjątków try ,catch.
To nie brzmi jak dobry design. Jeśli obiekt Triangle nie został utworzony, to czemu chciałbyś móc na nim wywoływać metody?

0

Ostatecznie chce to wszystko zamknąć w jakiejś pętli tak żeby punkty wpisywać seterami, do czasu aż nie będą dobre.

0

ale co i po co chcesz rzucać w trójkącie przyjmującym 3 punkty? Przecież nie będzie tu żadnych dziwnych stanów, a jeśli coś w obliczeniach nie będzie pasować to w metodzie to liczącej możesz rzucać.

Ps. settery są złe, zniszczą Cie.

3
  1. struktury typu trójkąt powinny mieć jak najmniej logiki w konstruktorach
  2. w konstruktorze wyjątek będzie upierdliwy, w strukturze podwójnie
  3. do rzucania używaj klasy pochodne od std::exception dzięki czemu nie będziesz musiał mieć w main catch dla każdego typu a jedynie dla właśnie tej klasy
    http://www.cplusplus.com/doc/tutorial/exceptions/
  4. jak chcesz mieć rozbudowany konstruktor to pomyśl lepiej o builderze:
    https://pl.wikipedia.org/wiki/Budowniczy_(wzorzec_projektowy)
2

Jak widzę jawne throw w konstruktorze to od razu zakładam, że z kodem jest coś nie tak. W tym przypadku lepiej byłoby użyć jakiegoś budowniczego i idealnie Boost.Optional albo std::optional bo wyjścia masz dwa, albo możesz stworzyć trójkąt z punktów albo nie, więc wyjątków tak na dobrą sprawę nie potrzebujesz. Ale jak pozostaniemy przy wyjątkach to wtedy tak (pseudokod):

class Triangle
{
public:
     // jak mamy optionale to create powinien go zwrocic
     static Triange crete(arguments){
          if(canCreateTriangleFromPoints(arguments)){
              return Triangle(arguments);
          }
          throw exception;
      }
     
private:
     Triangle(arguments);
     static bool canCreateTriangleFromPoints(arguments);
};


main(){
      try{
         auto tr = Triangle::create(arguments);
      }
      catch(exception)
      { // handle error}
      
}

EDIT
A jak potrzebujesz jakiś szczegółów co w obliczeniach poszło nie tak, to powinieneś mieć osobną klasę pod tytułem TrianglePointsInspector czy jakoś tak, która zwróci Ci szczegóły, a konstrukcja Triangle powinna pozostać binarna i idealnie z optionalami.

4

Przesadzacie. Ideą konstruktora jest stworzenie POPRAWNEGO obiektu. Kluczowym jest tu słowo "poprawnego". Jeśli z jakiegoś powodu konstruktor nie może utworzyć poprawnego obiektu, to jak najbardziej może rzucać wyjątek. A nawet powinien. Tutaj wyjątek powinien być rzucony, jeśli warunek istnienia trójkąta nie zostanie spełniony.

0

Jeśli weźmiemy pod uwagę co ten konstruktor musi sprawdzać:
https://stackoverflow.com/a/2049593

i założymy na chwilę że w C++ obecnie ogólnie projektuje i programuje się głównie dla wydajności, to nagle staje się oczywiste że konstruktor takiego rodzaju jest co najmniej wątpliwy.

Nie wyobrażam sobie przetwarznia miliona trójkątów z takimi konstruktorami.
Chyba że mówimy o programie edukacyjnym dla dzieci - wtedy OK.

Można oczywiście sobie zrobić otagowany konstruktor jeśli wzorzec budowniczego to za duży kłopot.
http://www.cplusplus.com/forum/general/151801/

Teoretycznie narzut takiego tagowania jest zerowy - ale nie sprawdzałem.

0

Dziwne pytanie. Przecież skoro tworzysz obiekt w try to wszystkie działania na nim też muszą się w nim zawrzeć. No bo jak? Będziesz pracować na niezainicjowanym obiekcie? Zupełnie abstrachując od tego, że w kodzie jest bład i raczej miało być a.x == 2 (bo a.x = 2 jest zawsze 2 czyli prawda) i od tego, że trudno mi sobie wyobrazić, żeby taki konstruktor miał rzucić wyjątek, może w skrajnym przypadku, jeśli wszystkie punkty leżą na jednej prostej, bo wtedy ciężko mówić o trójkącie.
Generalnie odradzam wpychanie rozwiązań na siłę, żeby poćwiczyć. A nuż nauczyłbyś się pisać bez wyjątków. Obsypali by cię złotem. xD A tak na serio, to skup się na tym, żeby zrobić coś użytecznego, bo to jest istotne w programowaniu. A wyjątki to raczej zło konieczne. :P

0
vpiotr napisał(a):

Nie wyobrażam sobie przetwarznia miliona trójkątów z takimi konstruktorami.
Chyba że mówimy o programie edukacyjnym dla dzieci - wtedy OK.

Jeśli robisz aplikację graficzną, to jasne że tego nie będziesz sprawdzał, bo tu wydajność jest na pierwszym miejscu. Ale raczej sam nie podajesz punktów, żeby stworzyć trójkąt, tylko modele przychodzą z zewnątrz. Więc to by było raczej bez sensu. Bo model z zewnątrz zawsze będzie się składał z poprawnych trójkątów. W innym przypadku (aplikacja niegraficzna, gdzie sam podajesz punkty) można coś takiego zrobić. Jeśli sprawdzanie faktycznie za długo trwa (nie optymalizuj przedwcześnie), to zawsze można je włączyć tylko w wersji debugowej.

W builder też nie po to powstał. To byłoby zbytnie komplikowanie banalnego problemu (łamanie zasady KISS).

0
Juhas napisał(a):
vpiotr napisał(a):

Nie wyobrażam sobie przetwarznia miliona trójkątów z takimi konstruktorami.
Chyba że mówimy o programie edukacyjnym dla dzieci - wtedy OK.

Jeśli robisz aplikację graficzną, to jasne że tego nie będziesz sprawdzał, bo tu wydajność jest na pierwszym miejscu. Ale raczej sam nie podajesz punktów, żeby stworzyć trójkąt, tylko modele przychodzą z zewnątrz. Więc to by było raczej bez sensu. Bo model z zewnątrz zawsze będzie się składał z poprawnych trójkątów.

Dokładnie tak.

W innym przypadku (aplikacja niegraficzna, gdzie sam podajesz punkty) można coś takiego zrobić. Jeśli sprawdzanie faktycznie za długo trwa (nie optymalizuj przedwcześnie), to zawsze można je włączyć tylko w wersji debugowej.

Nigdy nie rozumiałem tego hasła, tzn. "premature optimization is the root of all evil". Dla mnie jest idiotyczne.
Tzn. może ma znaczenie dla juniorów którzy robią jakieś mikro-optymalizacje albo nie zaglądają do ASM, to się zgodzę.
Ale wg mnie nie ma zastosowania w sytuacji gdy można mieć (lub nie) 12 nadmiarowych operacji arytmetycznych przy podstawowej operacji bazowej struktury danych.

W builder też nie po to powstał.

Nie wiem po co powstał, nie znam anegdot z tym związanych. W każdym razie:

The builder pattern is a Gang of Four design pattern. This is a creational pattern as it is used to control class instantiation. The builder pattern is used to create complex objects with constituent
parts that must be created in the same order or using a specific algorithm. An external class, known as the director, controls the construction algorithm.
http://www.blackwasp.co.uk/Builder.aspx

I mi tu to pasuje. A że w książkach nie ma mowy o walidacji w tym wzorcu - no trudno.

To byłoby zbytnie komplikowanie banalnego problemu (łamanie zasady KISS).
Zbytnie komplikowanie to wstawianie exception w konstruktor figury geometrycznej.

I żebyśmy się dobrze zrozumieli - rzucanie wyjątku z konstruktora jest OK - tylko nie z czegoś takiego, pls.

0
vpiotr napisał(a):

W innym przypadku (aplikacja niegraficzna, gdzie sam podajesz punkty) można coś takiego zrobić. Jeśli sprawdzanie faktycznie za długo trwa (nie optymalizuj przedwcześnie), to zawsze można je włączyć tylko w wersji debugowej.

Nigdy nie rozumiałem tego hasła, tzn. "premature optimization is the root of all evil". Dla mnie jest idiotyczne.
Tzn. może ma znaczenie dla juniorów którzy robią jakieś mikro-optymalizacje albo nie zaglądają do ASM, to się zgodzę.
Ale wg mnie nie ma zastosowania w sytuacji gdy można mieć (lub nie) 12 nadmiarowych operacji arytmetycznych przy podstawowej operacji bazowej struktury danych.

Daj spokój. Nie wiesz, ile potrwa sprawdzenie, czy punkty są poprawne. Czy 10 ms na trójkąt? A może 1 ms na trójkąt? A może sekunda? Nie wiadomo tego.

Czasem od razu widać, że fragment kodu nie będzie optymalny i np. wystarczy zamienić dwie linijki miejscami, żeby przyspieszyć od razu. Ale często jest tak, że po prostu chcemy coś napisać, a nie wymyślać sposób, który BYĆ MOŻE przyspieszy operację o pół sekundy, a zmarnujemy na to cały dzień. O to chodzi w przedwczesnej optymalizacji.

W builder też nie po to powstał.

Nie wiem po co powstał, nie znam anegdot z tym związanych. W każdym razie:

The builder pattern is a Gang of Four design pattern. This is a creational pattern as it is used to control class instantiation. The builder pattern is used to create complex objects with constituent
parts that must be created in the same order or using a specific algorithm. An external class, known as the director, controls the construction algorithm.
http://www.blackwasp.co.uk/Builder.aspx

I mi tu to pasuje. A że w książkach nie ma mowy o walidacji w tym wzorcu - no trudno.

No nie. Tu kluczowym słowem jest "complex". Czyli chodzi o jakiś skomplikowany obiekt, a nie o trójkąt z prostą walidacją. Builderów używa się gdy tworzysz obiekt, który musi być odpowiednio skonfigurowany i/lub zależy od innych obiektów. A nie do prostych obiektów, które wymagają prostej optymalizacji. To już jest utrudnianie sobie życia i pogwałcenie zasady KISS :)

To byłoby zbytnie komplikowanie banalnego problemu (łamanie zasady KISS).
Zbytnie komplikowanie to wstawianie exception w konstruktor figury geometrycznej.

I żebyśmy się dobrze zrozumieli - rzucanie wyjątku z konstruktora jest OK - tylko nie z czegoś takiego, pls.

A dlaczego nie? Przecież to upewnienie się, że można stworzyć poprawny obiekt. To jak najbardziej jest ok.

0

Wywalanie obiektu na kostruktorze ma sens, ale nie jezeli uzywamy bezposredniego wywolania. Lepiej zrobic jakas warstwe pomiedzy ktora ulatwi korzystanie z klasy

Wyobrazcie sobie taka sytuacje. Macie kod, ktory tworzy ten trojkat. I robicie

auto triangle = new Triangle(1,5,2);

I taki kod wam sie wywala (w sensie rzuca wyjatek) teraz musicie robic try catch wokol tego. W okol tworzenia jakiegos obiektu po przez kontstuktor.
Wymaga to

  1. posiadanie wiedzy jak dokladnie taka klasa jest zaimplementowana (nie mozliwe w przypadku DLL)
  2. czytanie dokumentacji

Oczywiscie, kazdy powinien takie rzeczy czytac i robic zanim napisze kod. Ale lepiej jest gdy kod podpowiada, ze dana rzecz moze wyrzucic wyjatek (szybciej i latwiej sie kodzi)

auto triangle = TriangleFactory.TryToCreate(1,5,2);

albo jeszcze lepiej, jezeli to ma byc kod wewnetrzny, to nie potrzebujemy wyjatkow (tam gdzie tworzymy triangle, oczywiscie factory wewnetrznie bedzie mialo obsluge wyjatku). Mozemy zwrocic obiekt z rezultem (to jest moj gust odnosnie tego)

auto resultOfCreation = TriangleFactory.Create(1,5,2);
if (resultOfCreation.successful)
{
  auto triangle = resultOfCreation.createdObject;
}
0
fasadin napisał(a):

Wywalanie obiektu na kostruktorze ma sens, ale nie jezeli uzywamy bezposredniego wywolania. Lepiej zrobic jakas warstwe pomiedzy ktora ulatwi korzystanie z klasy

I tak, i nie. To wszystko zależy od projektu. Bo może tworzysz wszystkie trójkąty raz w jednym miejscu.

Wyobrazcie sobie taka sytuacje. Macie kod, ktory tworzy ten trojkat. I robicie

auto triangle = new Triangle(1,5,2);

I taki kod wam sie wywala (w sensie rzuca wyjatek) teraz musicie robic try catch wokol tego. W okol tworzenia jakiegos obiektu po przez kontstuktor.
Wymaga to

  1. posiadanie wiedzy jak dokladnie taka klasa jest zaimplementowana (nie mozliwe w przypadku DLL)
  2. czytanie dokumentacji

Ad 1. Wystarczy przeczytać dokumentację.
Ad 2. Siadanie do nowej biblioteki bez czytania dokumentacji często skończy się porażką.

Oczywiscie, kazdy powinien takie rzeczy czytac i robic zanim napisze kod. Ale lepiej jest gdy kod podpowiada, ze dana rzecz moze wyrzucic wyjatek (szybciej i latwiej sie kodzi)

Zawsze można zrobić odpowiednie komentarze, które pojawią się np. w Intelisensie.

auto triangle = TriangleFactory.TryToCreate(1,5,2);

albo jeszcze lepiej, jezeli to ma byc kod wewnetrzny, to nie potrzebujemy wyjatkow (tam gdzie tworzymy triangle, oczywiscie factory wewnetrznie bedzie mialo obsluge wyjatku). Mozemy zwrocic obiekt z rezultem (to jest moj gust odnosnie tego)

auto resultOfCreation = TriangleFactory.Create(1,5,2);
if (resultOfCreation.successful)
{
  auto triangle = resultOfCreation.createdObject;
}

Czyli albo będziesz miał nullowy obiekt bez żadnej informacji o tym (będziesz musiał sprawdzać, czy trójkąt faktycznie się utworzył), albo będziesz musiał mieć optionale, co już wydaje się lepszym pomysłem i sprawdzać za każdym razem, czy trójkąt się utworzył. To już lepiej by było połączyć to w taki sposób:

Triangle * p;
if(TriangleFactory.TryCreate(1, 5, 2, &p)
{

}

(dla czepialskich, można oczywiście użyć smart pointerów tutaj :))

To wszystko tak naprawdę zależy od projektu. Bo jeśli, tak jak mówiłem, mamy trójkąty tworzone w pętli albo partiami, no to throw w konstruktorze jest odpowiednim wyjściem. Jeśli natomiast to projekt, w którym w wielu miejscach tworzy się jeden trójkąt, to można pomyśleć o czymś, co nie wymaga w każdym z tych miejsc obsługi wyjątków (to może być przerost formy nad treścią, bo w różnych miejscach możesz mieć różne wymagania). A może wystarczy obsługę wyjątków przenieść na odpowiedni poziom wyżej... Wszystko zależy od projektu.

2

@Juhas:

Daj spokój. Nie wiesz, ile potrwa sprawdzenie, czy punkty są poprawne. Czy 10 ms na trójkąt? A może 1 ms na trójkąt? A może sekunda? Nie wiadomo tego.

Zastanawiam się czy my rozmawiamy o PHP czy o C++?
Jak pracuję z C++ to operacja rzędu 1-10 ms jest dla mnie nieoptymalna na pewno.
Możemy się kłócić czy ma trwać 5 czy 10 ns.

1
vpiotr napisał(a):

Nigdy nie rozumiałem tego hasła, tzn. "premature optimization is the root of all evil". Dla mnie jest idiotyczne.

Może “root of all evil” to przesada, ale chodzi o to by nie marnować czasu na zbędne optymalizacje, zwłaszcza w komercyjnym projekcie — np. 10-krotne przyspieszenie jakiejś funkcji może się okazać bezsensowne, jeśli ta funkcja to jeden promil całkowitego czasu działania programu – i jej przyspieszenie w niczym nie pomoże z punktu widzenia użytkownika.

0
vpiotr napisał(a):

@Juhas:

Daj spokój. Nie wiesz, ile potrwa sprawdzenie, czy punkty są poprawne. Czy 10 ms na trójkąt? A może 1 ms na trójkąt? A może sekunda? Nie wiadomo tego.

Zastanawiam się czy my rozmawiamy o PHP czy o C++?
Jak pracuję z C++ to operacja rzędu 1-10 ms jest dla mnie nieoptymalna na pewno.
Możemy się kłócić czy ma trwać 5 czy 10 ns.

Jak to, że pracujesz w C++ ma się do tego, że widzisz ogromną różnicę między 1, a 10 ms? Ja też robię m.in. w C++ i dla mnie różnica pojawia się dopiero w momencie, czy coś się dzieje sekundę, czy 5. To nie jest kwestia języka, tylko projektu, nad którym się pracuje. Jeśli robisz jakiś super projekt czasu rzeczywistego, no to jasne, że nie będziesz tego sprawdzał, bo tu każda instrukcja się liczy. Ale jeśli tworzysz standardową aplikację biznesową lub coś w tym stylu, to tu nie potrzebujesz aż takich wyników.

To wszystko jest kwestią projektu, o czym już mówiłem wcześniej.

1

@Juhas: byc moze. O aplikacjach "biznesowych" w C++ tylko slyszalem z opowiesci. Ale wierze ze jeszcze takie istnieja.

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