Programm stürzt zufällig ab
-
Bengo schrieb:
Was sind denn diese besonderen Eigenschaften?
Ich weiß nicht ob SeppJ das damit meinte aber eine Eigenschaft von dynamische Speicherverwaltung ist natürlich, dass diese dynamisch ist! Wenn ich also erst zur Laufzeit weiß, wie viel Speicher ich für irgendwelche Daten brauche kommt man um dynamischen Speicher nicht drumherum. Allerdings helfen die Container aus der STL das man es nicht selbst machen muss.
Bengo schrieb:
Ist new und delete eigentlich ein wirklich absolutes nogo, oder nur eine sehr gute Richtlinie?
Wenn man es überall verbieten würde, dann könnte man ja nicht sowas wie
std::vectorimplementieren. Allerdings wurde das einem schon von den Entwicklern der Standardlibrary abgenommen, sodass man selbst eigentlich kaum noch new/delete schreiben braucht. Wenn man unbedingt in seiner Klasse Speicherverwaltung selbst machen möchte kann man ein new im Konstruktor und ein delete im Destruktor nutzen. Zwei Pointer in einer Klasse sind bereits nicht mehr trivial!
-
unique_philosoph schrieb:
Rohe Pointer sind ein Nogo! Dein "Anwendungsfall" ist ein Fall für unique_ptr+release(). Denn du willst deine Objekte wahrscheinlich zerstören, wenn eine Exception geworfen wird und nur freigeben, wenn nicht.
Warum
unique_ptrwenn man es auch auf dem Stack machen kann? Außerdem was soll der unterschied zwischen zerstören und freigeben sein? Man will natürlich, dass Objekte immer freigegeben werden!Bengo schrieb:
Würde ich mein Problem nun am besten mit einem Smartpointer lösen, oder besser der Klasse eine methode zum überschreiben mitgeben?
Die einfachste Variante für dein Programm sähe so aus:
for (int i = 4; i < 1000; ++i) { Faktor fac(i); // [...] }Hier ist allerdings der Nachteil, dass in jedem Schleifendurchlauf immer ein neues Objekt angelegt wird. Je nachdem wie groß dein Faktor Objekt ist könnte sowas schneller sein:
Faktor fac; for (int i = 4; i < 1000; ++i) { fac.set(i); // [...] }Smart Pointer brauchst du hier nicht.
-
sebi707 schrieb:
unique_philosoph schrieb:
Rohe Pointer sind ein Nogo! Dein "Anwendungsfall" ist ein Fall für unique_ptr+release(). Denn du willst deine Objekte wahrscheinlich zerstören, wenn eine Exception geworfen wird und nur freigeben, wenn nicht.
Warum unique_ptr wenn man es auch auf dem Stack machen kann? Außerdem was soll der unterschied zwischen zerstören und freigeben sein? Man will natürlich, dass Objekte immer freigegeben werden!
Unglückliche Wortwahl, mit freigeben habe ich "nicht zerstören" gemeint. Und ich bezog mich auf den Anwendungsfall von Mechanics, der das aus irgendeinem Grund vorausgesetzt hat.
sebi707 schrieb:
Wenn man es überall verbieten würde, dann könnte man ja nicht sowas wie std::vector implementieren.
Wieso, std::vector ist ein perfekter Nutzungszweck für unique_ptr!
struct vector { unique_ptr<aligned_storage<sizeof(T), alignof(T)>[]> data; size_t len; size_t alloc; void resize(int n) { unique_ptr<aligned_storage<sizeof(T), alignof(T)> new_data(new aligned_storage<sizeof(T), alignof(T)>[n]); uninitialized_copy(...); // daten rüberkopieren data = move(new_data); alloc = n; } };
-
sebi707 schrieb:
Die einfachste Variante für dein Programm sähe so aus:
for (int i = 4; i < 1000; ++i) { Faktor fac(i); // [...] }Hier ist allerdings der Nachteil, dass in jedem Schleifendurchlauf immer ein neues Objekt angelegt wird. Je nachdem wie groß dein Faktor Objekt ist könnte sowas schneller sein:
Faktor fac; for (int i = 4; i < 1000; ++i) { fac.set(i); // [...] }Smart Pointer brauchst du hier nicht.
Problem ist aber, dass ich dann in meiner While Schleife erneut den Konstruktor aufrufen müsste, was ich nicht kann. Ich denke das sinnvollste ist wirklich diese Lösung und dann noch eine init_neu(int number) Methode.
-
Bengo schrieb:
Problem ist aber, dass ich dann in meiner While Schleife erneut den Konstruktor aufrufen müsste, was ich nicht kann.
Aber einen Zuweisungsoperator hast du doch?
fac = Faktor(number);sollte automatisch gehen.
-
unique_philosoph schrieb:
Unglückliche Wortwahl, mit freigeben habe ich "nicht zerstören" gemeint. Und ich bezog mich auf den Anwendungsfall von Mechanics, der das aus irgendeinem Grund vorausgesetzt hat.
Ich hab gar nichts vorausgesetzt, ich weiß nicht, was du da reininterpretierst. Und ich hab nichts über rohe Zeiger gesagt.
-
unique_philosoph schrieb:
Wieso, std::vector ist ein perfekter Nutzungszweck für unique_ptr![cpp]
In deinem Beispiel steht aber noch ein
new. Du hättestmake_uniquebenutzen sollen! Aber irgendwo muss am Ende aber auf jeden Fall einnewstehen müssen.Was soll eigentlich das ganze aligned_storage Klimbim? Wenn ich
new T[N]schreibe ist das ja wohl schon passend aligned für T.
-
unique_philosoph schrieb:
Bengo schrieb:
Problem ist aber, dass ich dann in meiner While Schleife erneut den Konstruktor aufrufen müsste, was ich nicht kann.
Aber einen Zuweisungsoperator hast du doch?
fac = Faktor(number);sollte automatisch gehen.
Also mein Programm würde dann so aussehen:
int lastNumber = 0; for (int i = 20; i < 91000; ++i) { Faktor fac(i); int counter = 0; while(1) { int number = fac.getNextNumber(); counter ++; fac = Faktor(number); if (number == lastNumber) { cout << i << ": Geht auf prim " <<counter << endl; lastNumber = number; break; } lastNumber = number; if (number > 100000) { cout << i << ": Zu groß zum Berechnen "<<counter << endl; break; } counter ++; } }Den zuweisungsoperator kann ich doch dann so definieren und implementieren lassen?:
Faktor& operator=(const Faktor&& rhs)=default;
-
unique_philosoph schrieb:
Bengo schrieb:
Problem ist aber, dass ich dann in meiner While Schleife erneut den Konstruktor aufrufen müsste, was ich nicht kann.
Aber einen Zuweisungsoperator hast du doch?
fac = Faktor(number);sollte automatisch gehen.
Super! Statt jeden Schleifendurchlauf ein neues Objekt zu erstellen, erstellen wir immer noch jeden Schleifendurchlauf ein Objekt und kopieren dieses noch zusätzlich!
-
sebi707 schrieb:
unique_philosoph schrieb:
Wieso, std::vector ist ein perfekter Nutzungszweck für unique_ptr![cpp]
In deinem Beispiel steht aber noch ein
new. Du hättestmake_uniquebenutzen sollen! Aber irgendwo muss am Ende aber auf jeden Fall einnewstehen müssen.make_unique ruft den Konstruktor auf und das will ich nicht.
Was soll eigentlich das ganze aligned_storage Klimbim? Wenn ich
new T[N]schreibe ist das ja wohl schon passend aligned für T.Ja, aber das ruft den Konstruktor auf (für allgemeine T) und das will ich nicht.
@Mechanics: Ich wollte es nur klarstellen. In dem Zitat über deinem Post stand was von new/delete.
-
Ich werfe mal std::allocator (bzw. Allocators im Allgemeinen) in den Raum. schließlich ist es dieser, der intern von den ganzen Smartpointern und Containern genutzt wird, sofern man ihnen nichts anderes vorgibt. Klar läuft dieser intern letztlich auf so etwas wie aligned_storage und ein placement new hinaus. Aber so lange man nicht tatsächlich einen eigenen Allocator implementiert, wird man immer noch keinen Kontakt mit new/delete haben. Einen eigenen Container zu implementieren reicht als Grund noch nicht aus. Die Aufgabe eines Containers ist das Verwalten seiner Elemente, nicht das Beschaffen von Speicher.
-
Bengo schrieb:
Den zuweisungsoperator kann ich doch dann so definieren und implementieren lassen?:
Faktor& operator=(const Faktor&& rhs)=default;Nein, da hast du jetzt eine konstante RValue Referenz als Parameter drin und das macht nicht wirklich Sinn. Außerdem kriegst du die Operatoren alle schon automatisch, ohne das du die
=defaultsetzt.Sinnvoller für dich wäre wohl ein
operator=, welcher einen int akzeptiert und dann das gleiche macht wie dein Konstruktor (minus eventuell Speicherreservierung). Wie sieht deine Faktor Klasse überhaupt aus? Was für Membervariablen hat die Klasse und was macht dein Konstruktor? Nicht das die Klasse überhaupt nur einen int enthält, denn dann lohnt die ganze Optimierung nicht. Mit diesemoperator=könntest du dann sowas schreiben:Faktor fac; for (int i = 4; i < 1000; ++i) { fac = i; // [...] }unique_philosoph schrieb:
Ja, aber das ruft den Konstruktor auf (für allgemeine T) und das will ich nicht.
OK, seh ich ein.
-
So?
class Faktor { public: Faktor(int number); ~Faktor(); int getNextNumber(void); Faktor& operator=(int number); private: Faktor(const Faktor& orig); void setPrime(const int prime,const int expo); std::vector<PrimePair> primes; Primes p; void init(int number); };Faktor::Faktor(int number) { init(number); } void Faktor::init(int number) { primes.erase(primes.begin(), primes.end()); int counter = 0; int bound = floor(sqrt(number))+1; for(auto &i : p.primes) { if (i > bound) { break; } while (number % i == 0) { number = number / i; ++counter; } if (counter != 0) { setPrime(i, counter); counter = 0; } } } Faktor& Faktor::operator =(int number) { init(number); return *this; } }Das funktionert nun endlich so wie es soll. Und es ist tatsächlich merklich schneller geworden, obwohl ich dachte der großteil geht für die eigentliche Berechnung drauf.
Bin auch dumm, hab vorher meine primzahlliste immer wieder neu erstellt.
-
Was macht
setPrime? Du musst bei demoperator=natürlich beachten, dass du den Inhalt diverservectorlöscht, falls du damit nichts mehr anfangen kannst. Sowas wie schonmal berechnete Primzahlen kann man natürlich behalten. Du musst nur sehen, dass deine Funktionen mit schon teilweise befüllten Vektoren klar kommen.
-
sebi707 schrieb:
Nein, da hast du jetzt eine konstante RValue Referenz als Parameter drin und das macht nicht wirklich Sinn. Außerdem kriegst du die Operatoren alle schon automatisch, ohne das du die
=defaultsetzt.Ja sehe was du meinst, in der Referenz ist es aber möglich http://en.cppreference.com/w/cpp/language/move_operator
-
sebi707 schrieb:
Was macht
setPrime? Du musst bei demoperator=natürlich beachten, dass du den Inhalt diverservectorlöscht, falls du damit nichts mehr anfangen kannst. Sowas wie schonmal berechnete Primzahlen kann man natürlich behalten. Du musst nur sehen, dass deine Funktionen mit schon teilweise befüllten Vektoren klar kommen.Set Prime setzt ein Object mir Primzahl und Exponent in den Vector. Hab bei der init Funktion nun noch einen Befehl reingemacht, der den vektor vorher komplett leert.
-
Bengo schrieb:
Ja sehe was du meinst, in der Referenz ist es aber möglich http://en.cppreference.com/w/cpp/language/move_operator
Der move Operator ist aber ohne const.
Bengo schrieb:
Hab bei der init Funktion nun noch einen Befehl reingemacht, der den vektor vorher komplett leert.
Es gibt eine clear Funktion beim vector. Ist kürzer zu schreiben und möglicherweise schneller.
-
clear klingt gut, hätte das zweite google ergebnis nehmen sollen

Noch mal vielen Dank an alle, denke man Code ist nun im 21Jh angekommen
