Bin mir nicht sicher ob mein Konstruktor soweit ok ist und passt?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
|
Anzeige
|
C++ OOP ProblemBin mir nicht sicher ob mein Konstruktor soweit ok ist und passt?
Gast
#5494788
> Bin mir nicht sicher ob mein Konstruktor soweit ok ist und passt?
Du solltest Dir nicht sicher sein dass er OK ist und passt.
HTH
Gast
#5494863
else im ctor wäre unpassend wenn du immer allokieren möchtest - was passiert z.B. bei CDrawing(-1) Bert3 schrieb: > else im ctor wäre unpassend wenn du immer allokieren möchtest - was > passiert z.B. bei CDrawing(-1) Ich habe das Gefühl er meinte sowas:
wobei CDrawing(0) immer noch Ärger machen würde. Meine Empfehlung: den Teil
noch in eine private Funktion packen und dort noch entsprechend eine Fallunterscheidung für kleiner gleich 0 machen. #include<iostream>
using namespace std;
#include"CDrawing.h"
CDrawing::CDrawing(int initialSize ){
if(initialSize >10 || initialSize < 0){
initialSize = 10;
}
m_rectangles = new CRectangle [m_arraySize];
}
CDrawing::~CDrawing(){
delete [] m_rectangles;
}
Ich habe die else bedingung erst mal weg gemacht ,da der Speicher wohl
einfach ausserhalb der Bedingung gesetzt wird.
Aber was wollen die hier bei der Zusicherung ?
Im Klassen Diagramm ist ja bei der Zusicherung
arraySize > o gegeben usw
Gast
#5494894
"int" als Array-Größe zu nehmen ist eh eine schlechte Idee. Man verschenkt immer die Hälfte der möglichen Größe (Array-Größen können nie negativ werden, aber je nach Architektur durchaus die volle Größe eines vorzeichenlosen Typs annehmen). Was passiert auf AMD64-Systemen? Hier ist "int" meistens ein 32-bit-Integer, geht also bis 2^31-1, während Arrays aber tatsächlich bis 2^64 Elemente haben können. Die korrekte Lösung (welche auch alle Standard-Container wie std::vector nutzen) ist es, std::size_t aus dem Header <cstdint> zu verwenden. Da "int" vorgegeben ist, kannst du ja dazuschreiben warum das so besser ist. Unter Java nutzt man zwar "int" für alles und jedes, aber C++ ist nicht Java. Ja aber wir machen nur die Grundlagen von C++. Von uns will er immer nur Zusicherungen im Konstruktor und das wars . Was würdest du sagen wäre hier die korrekte Zusicherung?
Gast
#5494909
Lara C. schrieb: > Ja aber wir machen nur die Grundlagen von C++. Auch die kann man korrekt machen. "int" ist halt einfach falsch. Lara C. schrieb: > Von uns will er immer nur Zusicherungen im Konstruktor und das wars . Und dann darf man alles andere irgendwie machen? Willst du unbedingt etwas falsches lernen und angewöhnen, um es dann später wieder umzulernen? Lara C. schrieb: > Was würdest du sagen wäre hier die korrekte Zusicherung? Einfach nur dass die Array-Größe > 0 ist. m.M.n wäre zwar Größe 0 auch sinnvoll, aber das scheint nicht so gewollt zu sein. Kleiner 0 kann std::size_t ohnehin nicht werden - wenn man also versehentlich versucht etwas negatives zu übergeben, gibt es einen Compiler-Fehler und man sieht sofort was falsch ist - es handelt sich somit also um ein sichereres API. API ?
Gast
#5494915
Lara C. schrieb: > Ja aber wir machen nur die Grundlagen von C++. > > Von uns will er immer nur Zusicherungen im Konstruktor und das wars . > Was würdest du sagen wäre hier die korrekte Zusicherung? Welchen Wert kann denn bei deiner Implementierung m_arraysize annehmen? Wurde das so irgendwo gefordert? Oder ist das eher die Randbedingung für nextFree?! InitialSize ist ein Default-Wert für den Fall, daß man nichts anderes angegeben hat. Wenn man aber z.B. eine Zeichnung aus 1000 Objekten anlegen will, dann kann man sich 100 mal 10 Eintäge mehr Speicher holen und umkopieren ersparen, in dem man einfach gleich genug Platz anfordert. Natürlich nur, wenn der Konstruktor nicht auf 0..10 Objekte beschränkt ist. Die add-Methode muß sich dann um die Frage kümmern, ob noch Platz ist und ggf. m_arraysize (und das Array, auf das m_rectangles zeigt) vergrößern. Bei der Aufgabe geht es also um die (Minimal-)Implementierung eines dynamischen Containers für CRectangle Objekte. Ich glaube ,dass der Prof wahrscheinlich das möchte bei dieser Klausur:
#ifndef CDRAWING_H_
#define CDRAWING_H_
#include "CPoint.h"
#include "CScreen.h"
#include "CRectangle.h"
class CDrawing{
private:
CRectangle* m_rectangles;
int m_nextFree = 0;
int m_arraySize;
public:
CDrawing(int initialSize =10, int arraySize = 0);
~CDrawing();
void add(const CRectangle& rectangle);
void print();
void drawFiltered( CScreen& screen, char filter = 0);
};
#endif /* CDRAWING_H_ */
Ohne Gewähr
Gast
#5494947
Wohl eher "< 1". Es ist aber ziemlich dämlich, im Falle eines Programmierfehlers einfach irgendeinen Default anzunehmen. Wenn man schon eine negative Array-Größe übergibt, kann man keine Annahmen über zukünftige Zugriffs-Indices machen, und dann passiert irgendwas. Wenn schon sollte man eine Exception werfen oder ein "assert()" nutzen, oder natürlich negative Größen einfach vermeiden indem man std::size_t nutzt, was man sowieso auf jeden Fall tun sollte. Lara C. schrieb: > Ich glaube ,dass der Prof wahrscheinlich das möchte bei dieser Klausur: . > ... > if(m_arraySize < 0){ > > initialSize = 10; > m_arraySize = initialSize; > > > } > else{ > > m_rectangles = new CRectangle [m_arraySize]; > } > ... > > > Ohne Gewähr aber sicher will er nicht, daß der Pointer m_rectangles Initial bleibt, oder? initialSize = 10;
> m_arraySize = initialSize;
Hier schmeisse ich doch da sZeug von initial in Array ?
Und dann später speicher anlegen ?
Oder in else auch m_arraySize = initialSize; und dann Speicher anlegen ?
#include<iostream>
using namespace std;
#include"CDrawing.h"
CDrawing::CDrawing(int initialSize, int arraySize ){
m_arraySize = arraySize;
if(m_arraySize < 1){
initialSize = 10;
m_arraySize = initialSize;
}
else{
m_rectangles = new CRectangle [m_arraySize];
}
}
CDrawing::~CDrawing(){
delete [] m_rectangles;
}
Passt?
Dr. Sommer schrieb: > Lara C. schrieb: >> Ja aber wir machen nur die Grundlagen von C++. > > Auch die kann man korrekt machen. "int" ist halt einfach falsch. int ist nicht falsch, es ist nur nicht die beste Wahl. > Kleiner 0 kann std::size_t ohnehin nicht werden - wenn man also > versehentlich versucht etwas negatives zu übergeben, gibt es einen > Compiler-Fehler und man sieht sofort was falsch ist - es handelt sich > somit also um ein sichereres API. Nein. Wenn ich size_t als Parameter habe, bringt der Compiler keinen Fehler beim Übergeben einer negativen Zahl. Vielmehr wird eine sehr große positive Zahl daraus. Mich würden ja mal wieder zwei andere Sachen interessieren, denn diese Art des Aufgabenblattes hatten wir schon mehrfach: 1) Welche HS/FH/BA/Uni und Studiengang ist das? 2) Warum werden nicht die Kommilitonen/Assis/Profs gefragt? Im Sinne einer Prüfungsoptimierung sollte/muss man wohl so antworten, wie der Dozent das gerne hätte (und das kann hier keiner wissen). Das ist aber leider oft nicht state-of-the-art.
Gast
#5495381
Rolf M. schrieb: > Nein. Wenn ich size_t als Parameter habe, bringt der Compiler keinen > Fehler beim Übergeben einer negativen Zahl. Achja, der Fehler kommt nur wenn man per {} initialisiert; macht hier natürlich keiner. Dennoch finde ich es suboptimal einen Datentyp zu verwenden, der ungültige Werte zulässt, obwohl es eine bessere Wahl gibt. Rolf M. schrieb: > int ist nicht falsch, Es schränkt die Funktionalität unnötig ein; das fällt für mich unter "falsch". Genau solche Probleme behindern z.B. die Portierung von x86 auf x86_64... Wilhelm M. schrieb: > 1) Welche HS/FH/BA/Uni und Studiengang ist das? Diese Aufgabenblätter sehen überall gleich aus... Und sind überall gleich falsch :-) Wilhelm M. schrieb: > 2) Warum werden nicht die Kommilitonen/Assis/Profs gefragt? Die wissen es doch auch nicht. Lara C. schrieb: >
Vorausgesetzt der TO möchte C++ für die Praxis lernen und nicht nur diese Prüfungsaufgabe lösen. Dies wären meine Tipps als C++ Entwickler: 1) Im Konstruktor sollten alle Member initialisert werden. Entweder über die Initialisierungsliste oder (wie zum Teil schon passiert) in der Deklaration. m_rectangles sollte ein nullptr sein, sonst ist es bei einem Fehler im Konstruktor undefiniert und das kann zu vielen Fehlern führen (auch in anderen Modulen), die man dann nicht einfach wieder reproduzieren kann (z.B. Access Violation (gut zu finden), Heap Corruption (sehr sehr hässlich). 2) Wenn print oder drawFiltered das Objekt nicht verändern, dann würde ich diese Methoden als const markieren. Sonst kann man diese später über eine const CDrawing& nicht verwenden. 3) Den Kostruktor mit einem Parameter als explicit markieren, damit der Kompiler diesen nicht als konvertierenden Konstruktor verwenden kann. Sonst dürfte der Compiler damit einen int in ein CDrawing umwandeln. 4) m_rectangles, m_nextFree, m_arraySize machen ja nichts anderes, als der std::vector schon macht. Sogar eine initialSize (std::vector::reserve) hat dieser schon. Damit fällt die ganze händische Programmierung weg, vorallem wenn man das Array vergrößern muss. Außerdem sollte der std::vector auch schon so programmiert sein, dass der Speicher auch bei einer Exception wieder freigegeben wird. Hantiert man selbst mit new/delete, dann kann eine unerwartete Exception schnell zu einem Leak führen. > if(m_arraySize < 1){ > initialSize = 10; > m_arraySize = initialSize; > } > else{ > > m_rectangles = new CRectangle [m_arraySize]; > } > } ... > Passt? Nein. Du allokierst nix wenn arraySize < 1 (die Allokierung steht ja im 'else' Zweig)
Gast
#5497788
Dr. Sommer schrieb: > Rolf M. schrieb: >> int ist nicht falsch, > Es schränkt die Funktionalität unnötig ein; das fällt für mich unter > "falsch". Genau solche Probleme behindern z.B. die Portierung von x86 > auf x86_64... Aus performance Sicht können bspw. intel prozessoren (von denen weiß ich von einer intel Veranstaltung) ints in for loops besser optimiert werden. Wenn man sich sicher ist (oder checkt), dass size() nicht größer ist als numeric_limits<int>::max() (oder INT_MAX oder whatever) ist es sicherlich nicht falsch und ggf. eine bessere Wahl als size_t.
Gast
#5497805
Lks schrieb: > Wenn man sich sicher ist (oder checkt), dass size() nicht größer ist als > numeric_limits<int>::max() (oder INT_MAX oder whatever) ist es > sicherlich nicht falsch und ggf. eine bessere Wahl als size_t. Das Argument ist aber ein ganz anderes als "ich nehm immer int weil... ich das schon immer so gemacht hab... und weil das in Java auch so geht... und weil der Prof das auch so macht" :) Da muss man dann aber auch erst prüfen ob es ein Intel-Prozessor ist. Und auch hier würd ich dann eher unsigned int, oder noch besser uint32_t, nehmen. Solche CPU-abhängigen Mikro-Optimierungen dürften aber i.A. ziemlich selten nötig/sinnvoll sein. Da gibt's in den meisten Programmen noch ganz andere Dinge...
Gast
#5497818
Lks schrieb: > Aus performance Sicht können bspw. intel prozessoren (von denen weiß ich > von einer intel Veranstaltung) ints in for loops besser optimiert > werden. Kannst du das genauer erklären? Bei unsigned int verglichen mit int (32 Bit Werte auf 64 Bit Systemen) kann ich das verstehen (ausnutzen von signed-int-overflow-undefined-behavior), aber bei size_t mit 64 Bit?
Gast
#5497828
@Dr. Sommer Signed Typen in for loops werden bei intel (und wahrscheinlich auch bei anderen Prozessoren) schneller verarbeitet (for loops) als unsigned typen. Da geht es (je nach Problematik) nicht um mikrooptimierung sondern um zwei- bis dreistellige Prozentzuwächse. Ich wollte eigentlich nur sagen, dass ints in for loops (auch über stl container) nicht generell "falsch" sind ;)
Gast
#5497840
Lks schrieb: > Da geht es (je nach Problematik) nicht um mikrooptimierung > sondern um zwei- bis dreistellige Prozentzuwächse. Um wie viele Tausend Takte wird das denn langsamer? Die meisten Programme bestehen ja eher nicht zu 90% aus Schleifenzähler inkrementieren. Und worin besteht überhaupt der Unterschied zwischen signed und unsigned Addition? Das sind ja nichtmal verschiedene Instruktionen.
Gast
#5497940
Dr. Sommer schrieb: > Lks schrieb: >> Da geht es (je nach Problematik) nicht um mikrooptimierung >> sondern um zwei- bis dreistellige Prozentzuwächse. > > Um wie viele Tausend Takte wird das denn langsamer? Die meisten > Programme bestehen ja eher nicht zu 90% aus Schleifenzähler > inkrementieren. > Und worin besteht überhaupt der Unterschied zwischen signed und unsigned > Addition? Das sind ja nichtmal verschiedene Instruktionen. Da geht es eher nicht nur um das eigentliche Inkementieren (das auch, hier werden Überläufe anders gehandelt -> es muss ein Überlauf flag gesetzt werden) sondern auch um das Optimieren auf compiler Ebene (Vektorisieren, das Beispiel mit dem Performance Sprung war auf einer AVX-512 CPU - fairer Weise Intel Compiler auf Intel CPU :P). Kontex war Signalverarbeitung + Neuronale Netze auf Intel CPUs. Aber ich glaube wir schweifen etwas ab.
Gast
#5498132
Lks schrieb: > Da geht es eher nicht nur um das eigentliche Inkementieren (das auch, > hier werden Überläufe anders gehandelt -> es muss ein Überlauf flag > gesetzt werden) sondern auch um das Optimieren auf compiler Ebene > (Vektorisieren, das Beispiel mit dem Performance Sprung war auf einer > AVX-512 CPU - fairer Weise Intel Compiler auf Intel CPU :P). Kontex war > Signalverarbeitung + Neuronale Netze auf Intel CPUs. Hast du ein konkretes Beispiel?
Gast
#5498263
Dr. Sommer schrieb: > Und auch hier würd ich dann eher unsigned int, oder noch besser > uint32_t, nehmen. Das ist jetzt aber schon sehr akademisch: 32Bit vorausgesetzt und in der Annahme das ein CRectangle 4 Int-Werte enthält würde das Array bei der Anzahl INT_MAX an Elementen schon eine stattliche Größe von 32+ GB aufweisen. In der Praxis sollte nichts anderes verwendet werden als ein STL-Container, IMHO ;) mh schrieb: > Lks schrieb: >> Aus performance Sicht können bspw. intel prozessoren (von denen weiß ich >> von einer intel Veranstaltung) ints in for loops besser optimiert >> werden. > > Kannst du das genauer erklären? Bei unsigned int verglichen mit int (32 > Bit Werte auf 64 Bit Systemen) kann ich das verstehen (ausnutzen von > signed-int-overflow-undefined-behavior), aber bei size_t mit 64 Bit? :) Gut beobachtet. Ich musste grad schmunzeln weil ich EXAKT dieses Problem vor kurzem in einem Talk von Chandler Carruth gesehn hab. Hier der Link zu besagter Stelle: https://youtu.be/yG1OZ69H_-o?t=2358
Gast
#5498450
Na, da sagt er ja sogar dass size_t (64bit) auch effizient wäre. Braucht halt nur mehr Speicher. Solange man aber nicht sicher ist dass 32bit reichen braucht man das eh... M.K. B. schrieb: > Dies wären meine Tipps als C++ Entwickler: ... 5) Rule of three/five/zero berücksichtigen 6) Niemals `using namespace ...` in headern oder vor dem #include anderer header. 7) überlegen, ob sich default Funktionsparameter mit Überladungen vermeiden lassen. 8) forward declaraction verweden, wenn nur ein incomplete type in der declaration eines Typens ausreicht. 9) Nach dem Studium alles gelernte sofort vergessen und mit ein paar guten Bücher von Vorne anfangen ;-) mfg Torsten
Gast
#5498721
mh schrieb: > Lks schrieb: >> Da geht es eher nicht nur um das eigentliche Inkementieren (das auch, >> hier werden Überläufe anders gehandelt -> es muss ein Überlauf flag >> gesetzt werden) sondern auch um das Optimieren auf compiler Ebene >> (Vektorisieren, das Beispiel mit dem Performance Sprung war auf einer >> AVX-512 CPU - fairer Weise Intel Compiler auf Intel CPU :P). Kontex war >> Signalverarbeitung + Neuronale Netze auf Intel CPUs. > > Hast du ein konkretes Beispiel? Im Beispiel ging es um ein k-means clustering. In einer "size_t for loop" spuckt das Intel Performance Tool (Intel Advisor war das glaube ich) "Scalar loop. Not vectorized: unsigned types for induction variable and/or for lower/upper iteration bounds make loop uncountable Loop with unsigned induction variable" aus. Überschrift der Folie lautet "Compilers don't like unsigned integers". Auf den nächsten Folien wird die Struktur der Loop geändert und die loops auf std::ptrdiff_t umgestellt. Das bringt am Ende ein speedup von ca. Faktor 3 (AVX-2). Dann wird nicht mehr über ein array of structures (RGB pixel als structure und dann einen Array davon) sondern über eine Structure of arrays iteriert -> caching optimiert + effizienter vektorisiert -> bringt erneut einen speedup von Faktor 2, insgesamt Faktor ~6. Danach wird noch etwas weiter optimiert (ptrdiff_t durch int ersetzt, wenn ich das richtig sehe), sodass der compiler AVX 512 verwenden kann (erneut Faktor ~2 an speedup). Das Ganze fand in der Präsentation vorerst auf einen Kern statt. Und skalierte dann noch mit Anzahl der Kerne bei Parallelisierung mittels openmp. Als Hinweise kam dann noch: ... - Use small types to help vectorization - Use contiguous memory access ... Die Folien kommen von Georg Zitzlsberger (https://software.intel.com/en-us/user/420234) und sind tw. mit Quelltext versehen.
Gast
#5498785
Lks schrieb: > Im Beispiel ging es um ein k-means clustering. In einer "size_t for > loop" spuckt das Intel Performance Tool (Intel Advisor war das glaube > ich) "Scalar loop. Not vectorized: unsigned types for induction variable > and/or for lower/upper iteration bounds make loop uncountable Loop with > unsigned induction variable" aus. Überschrift der Folie lautet > "Compilers don't like unsigned integers". Die "hilfreiche" Warnung... hat in vielen Fällen nicht direkt etwas mit size_t zu tun und ist ohne wirklich konkretes Beispiel nutzlos. Der restliche Teil deines Beitrags lässt sich mit: Lks schrieb: > - Use small types to help vectorization > - Use contiguous memory access zusammenfassen. Diese Hinweise sind schön und gut, aber für einen Großteil aller Softwareprojekte irrelevant. Es wird zuerst korrekte Software geschrieben und dann optimiert wo es nötig ist. Nur in Software die zu langsam ist und in den <5% des Programms, die relevant für die Performance sind, muss man zwischen korrekt und schnell abwägen. Zum Schreiben korrekter Software gehört allerdings das Wählen der richtigen Datentypen. Ob unsinged der richtige Datentyp ist, ist dann wieder eine andere Frage, mit vielen pro und contra Argumenten, die jeder für sich selbst abwägen muss.
Gast
#5498800
mh schrieb: > Diese Hinweise sind schön und gut, aber für einen Großteil aller > Softwareprojekte irrelevant. > Es wird zuerst korrekte Software geschrieben und dann optimiert wo es > nötig ist. Nur in Software die zu langsam ist und in den <5% des > Programms, die relevant für die Performance sind, muss man zwischen > korrekt und schnell abwägen. > Zum Schreiben korrekter Software gehört allerdings das Wählen der > richtigen Datentypen. Ob unsinged der richtige Datentyp ist, ist dann > wieder eine andere Frage, mit vielen pro und contra Argumenten, die > jeder für sich selbst abwägen muss. Ich bin generell bei dir. Wie ich weiter oben schon schrieb: Lks schrieb: > Ich wollte eigentlich nur sagen, dass ints in for loops (auch über stl > container) nicht generell "falsch" sind ;) mh schrieb: > Nur in Software die zu langsam ist und in den <5% des > Programms, die relevant für die Performance sind, muss man zwischen > korrekt und schnell abwägen. An dieser Stelle möchte ich widersprechen. Korrekt und schnell wäre auch eine Möglichkeit ;) Ein int bspw. kann korrekt und schnell sein, wenn sichergestellt ist, dass der Container nicht zu groß ist.
Gast
#5498928
Dr. Sommer schrieb: > Und worin besteht überhaupt der Unterschied zwischen signed und unsigned > Addition? Das sind ja nichtmal verschiedene Instruktionen. Guck mal hier: CppCon 2016: Chandler Carruth “Garbage In, Garbage Out: Arguing about Undefined Behavior..." https://youtu.be/yG1OZ69H_-o?t=39m13s mh schrieb: > Nur in Software die zu langsam ist und in den <5% des > Programms, die relevant für die Performance sind, muss man zwischen > korrekt und schnell abwägen. In der Regel wird man eher zwischen schön und schnell abwägen. Schneller Code, der aber nicht das richtige Ergebnis bringt, ist ja nun eher sinnlos. Antwort schreibenBitte melde dich an, um einen Beitrag zu schreiben. |
Anzeige
|