Problem mit Software Architektur (C++)
-
Ishildur schrieb:
Wo ist der Unterschied? Ausser dass das Beispiel ohne "else" meiner Meinung nach weniger intuitiv lesbar ist?
Ist das dein ernst?
Ishildur schrieb:
float32 divide(float32 A,float32 B){ if(B == 0.0f) throw DivisionException(); else return A/B; }
Es ist deutlich intuitiver, dass else wegzulassen. Du prüfst du auf einen Sonderfall, und wenn diese Sonderfall eintritt wird sofort eine Exception geworfen und die Funktion endet. Bei einer Sonderfall-Überprüfung, die auch gleich die Funktion endet, schreibt man kein else.
Schönere Einrückung macht es deutlicher.
float32 divide(float32 A, float32B) { if (b == 0.0f) throw DivisionException(); return A/B; }
-
@Janjan
Ja du hast recht (natürlich auch die Andern, die dieses Beispiel kritisiert haben, was ein dummes Beispiel)!Wieso funktioniert diese simple Abstrakte Fabrik nach Lehrbuch einfach nicht?
#include <iostream> class IResource{ public: virtual int GetValue(void) const = 0x00; }; class IResourceFactory{ public: virtual IResource *NewInstance(void) = 0x00; }; class IHeightmapResource:public virtual IResource{ public: virtual int GetValue2(void) const = 0x00; }; class IHeightmapResourceFactory:public IResourceFactory{ public: virtual IHeightmapResource *NewInstance(void) = 0x00; }; class HeightmapResource:public IHeightmapResource{ public: int GetValue(void) const{return 5;} int GetValue2(void) const{return 10;} }; class HeightmapResourceFactory:public IHeightmapResourceFactory{ public: HeightmapResource *NewInstance(void){return new HeightmapResource();} }; int main(){ IResourceFactory *pFac = new HeightmapResourceFactory(); IResource *pRsc = pFac->NewInstance(); std::cout << pRsc->GetValue() << std::endl; std::cout << dynamic_cast<IHeightmapResource*>(pRsc)->GetValue2() << std::endl; std::cout << std::endl; delete pRsc; delete pFac; return 0; }
Resultat:
Kompiliert anstandslos und stürzt anschliessend bei delete pRsc mit einer BlockInUse Assertion ab
-
public: virtual int GetValue(void) const = 0x00;
Hehe lol, was soll denn die 0x00? Ist dir einfach nur 0 nicht hübsch genug?
Und(void)
ist auch ein unnützer Anachronismus. Du machst es gern kompliziert und schreibst unnötiges Zeug, was?Ishildur schrieb:
Kompiliert anstandslos und stürzt anschliessend bei delete pRsc mit einer BlockInUse Assertion ab
Ich sehe keinen Fehler im Code, bei Codepad läuft es auch durch.
-
Hehe lol, was soll denn die 0x00?
Mein Gott fängt das wieder an. Ich schreibe bei long 45l, bei float 3.4f bei Zahlen 0,1,2,3,4 und bei Adressen 0x00, 0x01, 0x02 das ist mein Stil basta! :p
Was ich gemerkt habe ist, dass wenn ich IHeightmapResource nicht virtuell von IResource erbe dann funktioniert es, aber wieso stürzt es bei virtueller Vererbung ab, das darf doch nicht wahr sein...
-
Statt leerer Parameterlisten schreibt man nicht void. Das ist kein eigener "Stil", das ist C.
-
Aber 0x00 ist bei pure virtual einfach falsch. Laut C++-Standard ist der pure-Specifier "=0;" und nichts anderes und entsprechend compiliert dir das auch nicht jeder Compiler (GCC beispielsweise nicht).
edit: Und ich weder sehe ich in deinem Beispiel einen offensichtlichen Fehler noch findet der Debugger einen. Ist das wirklich 1:1 das was bei dir abstürzt?
-
Ishildur schrieb:
Mein Gott fängt das wieder an. Ich schreibe bei long 45l, bei float 3.4f bei Zahlen 0,1,2,3,4 und bei Adressen 0x00, 0x01, 0x02 das ist mein Stil basta! :p
Ein pure specifier ist aber keine Adresse und hat offiziell die Form
= 0
. Alles andere ist falsch, basta!Man kann wenig Ahnung nicht mit starken Meinungen wettmachen, du sieht ja: du stolperst von einem Fettnäpfchen ins nächste.
-
Ein pure specifier ist aber keine Adresse und hat offiziell die Form = 0. Alles andere ist falsch, basta!
Ok, überzeugt, ich ändere es überall
Nein, das ist es 1:1. Sobald ich irgendwo eine virtuelle Vererbung habe stürzt das Teil ab. MSVS 2008
-
1:1
class IResource{ public: virtual int GetValue() const = 0; }; class HeightmapResource:public virtual IResource{ public: int GetValue() const{return 5;} }; int main(){ IResource *pRsc = new HeightmapResource(); delete pRsc; return 0; }
Stürzt beim delete ab, entferne ich das virtual bei der Vererbung von HeightmapResource auf IResource funktioniert alles bestens...
-
Mach mal den Destruktor auch virtuell.
-
JTZT GEHHTERR.... DAS GIBTS DOCH GAR NIDD....
Kannst du mir das erklären, irgendwie sehe ich das nicht so ganz...
-
Jockelx schrieb:
Mach mal den Destruktor auch virtuell.
Wollte es gerade auch schreiben, aber du warst schneller.
Ishildur schrieb:
Kannst du mir das erklären, irgendwie sehe ich das nicht so ganz...
Löscht du ein polymorphes Objekt über einen Zeiger auf seine Basisklasse, so wird logischerweise der Destruktor der Basisklasse aufgerufen. Ist dieser aber nicht virtuell, so wird der Destruktor der abgeleiteten Klasse nicht aufgerufen, was zu allerlei Problemen führen kann.
-
OK, soweit so gut, aber die destruktoren sind doch alle leer?
-
Ishildur schrieb:
Kannst du mir das erklären, irgendwie sehe ich das nicht so ganz...
Ein Destruktor ist grundsätzlich auch nichts anderes wie eine Funktion. Wenn du keinen virtuellen Destruktor hast und auf der Basisklasse das delete aufrufst, so wird nur der Destruktor der Basisklasse aufgerufen und die von den abgeleiteten Klassen nicht. Somit wird das Objekt nur unvollständig zerstört.
Das sind aber absolute C++ Grundlagen
Ist es eigentlich bei dir üblich, dass du des öfteren Klassen hast, welche nur pure virtual Funktionen haben?
Grüssli
-
Ishildur schrieb:
OK, soweit so gut, aber die destruktoren sind doch alle leer?
Der Kompiler kann aber eigener Code reinsetzen, was er auch tut. Um Vererbung, virtuelle Funktionen usw. usf. lauffähig zu bekommen, braucht es normalerweise noch etwas zusätzlichen Code. Solcher Code wird dann auch im Destruktor aufgeräumt, ohne dass du es zu sehen bekommst
Grüssli
-
Ja ich meine auch mehr, wieso es dann nur bei der virtuellen Verwerbung zu einem Problem kommt?
Ist es eigentlich bei dir üblich, dass du des öfteren Klassen hast, welche nur pure virtual Funktionen haben?
Ja, wieso? Nicht gut?
-
Ishildur schrieb:
Ja ich meine auch mehr, wieso es dann nur bei der virtuellen Verwerbung zu einem Problem kommt?
Es kann auch sonst zu Problemen kommen, es ist einfach undefiniertes Verhalten, wenn du es nicht machst. Und undefiniertes Verhalten ist eben undefiniert. Es kann mal funktionieren oder abstürzen, die Welt kann explodieren oder ein Antimaterie-Universum dabei entstehen und somit das unsrige gleich mitreissen und am Ende war nichts mehr ...
Ishildur schrieb:
Ist es eigentlich bei dir üblich, dass du des öfteren Klassen hast, welche nur pure virtual Funktionen haben?
Ja, wieso? Nicht gut?
Kann man sicherlich drüber streiten, aber dein bisherigen Codestil und deine Vorgehensweisen deute ich so, dass du von irgendeiner anderen Sprache schlecht beeinflusst wirst und probierst in C++ nicht C++ zu programmieren. Aber bisher waren es nur Indikatoren. Will also nichts unterstellen
Grüssli
-
@Dravere
Ich lerne gerne dazuAlso ich versuche in C++ objektorientiert und möglichst flexibel zu programmieren, so wie ich das in jeder anderen Sprache ebenfalls versuche. Mein Grundsatz ist, "Programmiere gegen Interfaces nicht gegen Implementationen". Daher die Interfaces. Beispiel: IResource, die sieht bei mir beim aktuellen Projekt folgendermassen aus:
// *********************************** interface "IResource" ********************************** // This interface defines a set of functionalities which must be implemented by every // Resource. // Author: Samuel Lörtscher // ******************************************************************************************** class IResource{ public: // ------------------------------------ interface methods ------------------------------------ virtual void Register(IResourceService *Service,IResourceReader *Reader) = 0; virtual void Load() = 0; virtual void Unload() = 0; virtual void Reload() = 0; virtual string *GetName() const = 0; virtual ResourceFamily GetFamily() const = 0; virtual IResourceService *GetOwner() const = 0; virtual IResourceReader *GetReader() const = 0; virtual uint32 RefCount() const = 0; virtual bool IsLoaded() const = 0; virtual ~IResource(){} // ------------------------------------------------------------------------------------------- }; // ********************************************************************************************
Jedem, der IResource benützt, kann es völlig egal sein, wie IResource diese Dinge intern organisiert. Ich muss bspw. einfach abfragen können, ob das Teil geladen ist oder. Ob er dann das in einem bool speichert, oder seinen Owner fragen muss oder was auch immer ist mir egal...
Dazu kommt der ResourceReader (Auch wieder nur ein Interface [only pure virtual methods]), dieser muss einfach die methoden Open, Read, Size, Close machen können. Ob der noch einen WinSock benötigt oder ein FileHandle oder was weis ich, müssen alle die Programmkomponenten, welche IResourceReader in irgendeiner Form benutzen nicht wissen...
Oder hast du das wegen anderen Dingen gemeint?
-
Mußt nur gelegentlich schauen, ob sich der Aufwand überhaupt gelohnt hat.
-
Es ist halt ein bisschen komisch, da eine Klasse die nur pure virtual Funktionen hat, keinerlei Funktionalität hat. Sie gibt ein paar Namen vor, macht sonst aber nichts was diesen Namen Bedeutung gibt.
Gegenfrage: Inwiefern unterscheiden sich diese beiden Klassen?
class fahrzeug { virtual ~fahrzeug(){} virtual void beschleunigen()=0; virtual void abbremsen()=0; }; class ballaballapingpong { virtual ~ballaballapingpong(){} virtual void fnvgkjengdjkfgvd()=0; virtual void fdjfn4j4nwksdfnkd()=0; };
Gar nicht, oder?
Erst wenn man ein bisschen Funktionalität dazu packt, bekommt die Fahrzeugklasse einen Sinn:
class fahrzeug { virtual ~fahrzeug(){} virtual void beschleunigen()=0; virtual void abbremsen()=0; virtual void einfache_fahrt { beschleunigen(); abbremsen(); } };