Programmierstil so Ok?
-
Hallo Leute
Finde dieses Forum sehr hilfreich und schaue fast jeden Tag vorbei.
Seit einiger Zeit programmiere ich (Autodidakt) mit dem BCB5.
Anfangs war mein Code ein wirres Durcheinander von Funktionen,
deren Aufgabe ich später nicht mehr nachvollziehen konnte.
Damals stand einfach das Funktionieren (so schnell wie möglich) im Vordergrund.
Als ich aber merkte, das einige Sachen immer wieder mal auftauchen, bin ich
dazu übergegangen, Klassen für meine Belänge zu schreiben.
Dadurch ist der Code dann letztendlich übersichtlicher und fehlerunanfälliger
geworden. Ich habe mir auch strikte Kommentierung des Codes angewöhnt.Nun zu meiner eigentlichen, Frage(n):
Was haltet ihr (die echten Cräcks hier im Forum) von meinem Programmierstil?
Was sticht euch vielleicht direkt ins Auge und könnte anders besser gemacht
werden?
Das untenstehende Programm funktioniert tadellos, darum gehts nicht.
Ich würde bitte einfach gerne eure Meinung zu meinem Programmierstil wissen.Unit1.h:
//--------------------------------------------------------------------------- #ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------- #include <Classes.hpp> #include <Controls.hpp> #include <StdCtrls.hpp> #include "MDB_Class.cpp" #include "Screen_Capture_Class.cpp" #include <Forms.hpp> #include <ExtCtrls.hpp> #include <ActnList.hpp> //--------------------------------------------------------------------------- class TForm1 : public TForm { __published: // Von der IDE verwaltete Komponenten TButton *Btn_Start; TTimer *MainLoop; TButton *Btn_Stop; TPanel *Panel1; TImage *Image1; TEdit *Edit1; TButton *Btn_Verwerfen; TActionList *ActionList1; TAction *SetButtonsRec; TAction *SetButtonsStop; void __fastcall Btn_StartClick(TObject *Sender); void __fastcall MainLoopTimer(TObject *Sender); void __fastcall Btn_StopClick(TObject *Sender); void __fastcall Btn_VerwerfenClick(TObject *Sender); void __fastcall SetButtonsRecExecute(TObject *Sender); void __fastcall SetButtonsStopExecute(TObject *Sender); private: // Anwender-Deklarationen public: // Anwender-Deklarationen ScreenCaptureClass Scc; // Instanz der Klasse ScreenCaptureClass erzeugen MDB Db; // Instanz der Klasse MDB erzeugen __fastcall TForm1(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
Unit1.cpp:
// MyFreeCamCap (c) 2009 by Longo //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- // Grundwerte festlegen __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Scc.SpeicherPfad="D:\\Screen Captures\\"; //Speicherpfad für Bilder festlegen Db.SetMDBPathAuto(); //DB Pfad festlegen (hier Projektverzeichnis) Db.SetMDBFile("Archiv.ini"); //Name der DB (DB wird erzeugt wenn nicht vorhanden) Panel1->DoubleBuffered=true; //muss wegen flackerns sein SetButtonsStop->Execute(); //Buttons setzen MainLoop->Interval=62; //1000:63 = ca.16 Bilder pro Sekunde anzeigen/abspeichern } //--------------------------------------------------------------------------- // Hauptschleife (Timer) void __fastcall TForm1::MainLoopTimer(TObject *Sender) { Scc.ScreenCap(Image1,23,214,320,240); // Bild capturen. if (Scc.isRecording==true){ // Wenn im Aufnahmemodus, Scc.SavePic(Image1,Edit1->Text); // dann Bild speichern. Caption="Bild "+IntToStr( // Anzahl errechnen und Scc.PicCount-Scc.PicCountOld); // im Fenstertitel anzeigen. // Blinken während der Aufnahme: if (Scc.Toggle==1) // Wenn Toggle = 1, Form1->Color=clRed; // Formfarbe = rot. if (Scc.Toggle==0) // Wenn Toggle = 0, Form1->Color=clBtnFace; // Formfarbe = grau. } } //--------------------------------------------------------------------------- // REC Funktion void __fastcall TForm1::Btn_StartClick(TObject *Sender) { if (Edit1->Text==""){ // Wenn kein Name im Textfeld if (Application->MessageBox(" Kein Name im Textfeld ! Soll ein Dummy-Name verwendet werden?", "MyFreeCamsCap: Aufnahme nicht möglich !", MB_OKCANCEL)==IDOK) Edit1->Text="Dummy"; } if (Edit1->Text!=""){ // Wenn Name im Textfeld vorhanden Scc.PicCount=StrToInt(Db.GetDsValue(Edit1->Text)); // Bisherige Anzahl aus DB holen Scc.PicCountOld=Scc.PicCount; // Alte Anzahl zwischenspeichern SetButtonsRec->Execute(); // Buttons setzen Scc.isRecording=true; // Aufnahmemodus Start } } //--------------------------------------------------------------------------- // Übernehmen Funktion void __fastcall TForm1::Btn_StopClick(TObject *Sender) { Scc.isRecording=false; // Aufnahmemodus Stop Db.SetDs(Edit1->Text,Scc.PicCount); // Neue Anzahl in DB schreiben Scc.PicCount=0; // Bildzähler auf Null setzen Caption="MyFreeCamCap (c) 2009 by Longo"; // Fenstertitel setzen SetButtonsStop->Execute(); // Buttons setzen Form1->Color=clBtnFace; // Farbe auf grau setzen Scc.Toggle=0; // Toggle auf null setzen } //--------------------------------------------------------------------------- // Verwerfen Funktion void __fastcall TForm1::Btn_VerwerfenClick(TObject *Sender) { Scc.isRecording=false; // Aufnahmemodus Stop Caption="MyFreeCamCap (c) 2009 by Longo"; // Fenstertitel setzen for (long i=Scc.PicCountOld;i<Scc.PicCount;i++){ Scc.DeletePic(Edit1->Text,i); // Bilder löschen } Db.SetDs(Edit1->Text,Scc.PicCountOld); // Neue Anzahl in DB schreiben SetButtonsStop->Execute(); // Buttons setzen Form1->Color=clBtnFace; // Farbe auf grau setzen Scc.Toggle=0; // Toggle auf null setzen } //--------------------------------------------------------------------------- // Button Kontroll Funktion bei REC void __fastcall TForm1::SetButtonsRecExecute(TObject *Sender) { Btn_Start->Enabled=false; // REC Button deaktivieren Btn_Stop->Enabled=true; // Übernehmen Button aktivieren Btn_Verwerfen->Enabled=true; // Verwerfen Button aktivieren } //--------------------------------------------------------------------------- // Button Kontroll Funktion bei Übernehmen/Verwerfen void __fastcall TForm1::SetButtonsStopExecute(TObject *Sender) { Btn_Start->Enabled=true; // REC Button aktivieren Btn_Stop->Enabled=false; // Übernehmen Button deaktivieren Btn_Verwerfen->Enabled=false; // Verwerfen Button deaktivieren } //---------------------------------------------------------------------------
MDB_Class.cpp:
// MDB_Class 2009 by Longo //********************************************************************* #include <ExtCtrls.hpp> class MDB { public: AnsiString MDBPath; // DB Verzeichnis AnsiString MDBFile; // DB Dateiname void SetMDBPath(AnsiString MDBPath); // DB Verzeichnis manuell wählen void SetMDBFile(AnsiString MDBFile); // DB Dateiname bestimmen void SetMDBPathAuto(void); //Automatisch das Projectverzeichnis als DB Verzeichnis wählen void SetDs(AnsiString DSName, AnsiString DSValue);//Wert in Datensatz schreiben void DelDs(AnsiString DSName); //Datensatz löschen (wenn vorhanden) bool FindDs(AnsiString DSName); // Nach Datensatz suchen AnsiString GetDsValue(AnsiString DSName); //Wert aus Datensatz lesen. long GetDBCount (void); //Anzahl der Datensätze in der DB ermitteln void AddToValue(AnsiString DsName, AnsiString AddValue);//Addiert einen ALPHA-NUMERISCHEN Wert zu einem ALPHA-NUMERISCHEN DsValue void SetAllDsValuesToZero(); }; //--------------------------------------------------------------------------- //MDB Methoden : //--------------------------------------------------------------------------- void MDB::SetMDBPath(AnsiString Path) // DB Verzeichnis manuell wählen { MDB::MDBPath=Path; } //--------------------------------------------------------------------------- void MDB::SetMDBFile(AnsiString Filename) // DB Dateiname bestimmen { if (!FileExists(MDB::MDBPath + Filename)){ //Wenn DB noch nicht vorhanden TStringList *sl=new TStringList(); //dann neue DB anlegen sl->SaveToFile(MDB::MDBPath + Filename); delete sl; } MDB::MDBFile=Filename; } //--------------------------------------------------------------------------- void MDB::SetMDBPathAuto(void) //Automatisch das Projectverzeichnis { //als DB Verzeichnis wählen MDB::SetMDBPath(ExtractFilePath(Application->ExeName)); } //--------------------------------------------------------------------------- void MDB::SetDs(AnsiString DataSetName, AnsiString DataSetValue) { if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden //Wert in Datensatz schreiben TStringList *sl=new TStringList(); //es wird ein neuer Datensatz erzeugt sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile); //wenn DataSetName noch nicht vorhanden bool SchonDa=false; for (long i=0;i<sl->Count;i=i+2){ if (sl->Strings[i]==DataSetName){ sl->Strings[i+1]=DataSetValue; SchonDa=true; } } if (SchonDa==false){ sl->Add(DataSetName); sl->Add(DataSetValue); } sl->SaveToFile(MDB::MDBPath+MDB::MDBFile); delete sl; } } //--------------------------------------------------------------------------- void MDB::DelDs(AnsiString DelDsName) //Datensatz löschen (wenn vorhanden) { if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden TStringList *sl=new TStringList(); sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile); for (long i=0;i<sl->Count;i=i+2){ if (sl->Strings[i]==DelDsName){ sl->Delete(i); sl->Delete(i); } } sl->SaveToFile(MDB::MDBPath+MDB::MDBFile); delete sl; } } //--------------------------------------------------------------------------- bool MDB::FindDs(AnsiString FindDsName) // Überprüfen ob Datensatz vorhanden { bool SchonDa=false; if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden //(übergibt true wenn vorhanden) TStringList *sl=new TStringList(); sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile); for (long i=0;i<sl->Count;i=i+2){ if (sl->Strings[i]==FindDsName){ SchonDa=true; } } delete sl; } return SchonDa; } //--------------------------------------------------------------------------- AnsiString MDB::GetDsValue(AnsiString DataSetName) { AnsiString DataSetValue="0"; if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden //Wert aus Datensatz lesen. TStringList *sl=new TStringList(); //Es wird "0" sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile);//zurückgegeben wenn DataSetName nicht vorhanden for (long i=0;i<sl->Count;i=i+2){ if (sl->Strings[i]==DataSetName){ DataSetValue=sl->Strings[i+1]; } } delete sl; } return DataSetValue; } //--------------------------------------------------------------------------- long MDB::GetDBCount(void) //Anzahl der Datensätze in der DB ermitteln { long DBCount=0; if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden TStringList *sl=new TStringList(); sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile); DBCount=sl->Count/2; //Anzahl der Lines in der DB geteilt durch 2 delete sl; } return DBCount; } //--------------------------------------------------------------------------- void MDB::AddToValue(AnsiString DataSetName, AnsiString AddDataSetValue) { //Addiert einen NUMERISCHEN Wert zu einem NUMERISCHEN DsValue mit StrToInt() //Wenn DataSetName nicht vorhanden, wird ein neuer DS erzeugt. if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden TStringList *sl=new TStringList(); sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile); long DataSetValueLong=0; long AddDataSetValueLong=0; bool SchonDa=false; for (long i=0;i<sl->Count;i=i+2){ if (sl->Strings[i]==DataSetName){ DataSetValueLong=StrToInt(sl->Strings[i+1]); //String in long umwandeln AddDataSetValueLong=StrToInt(AddDataSetValue);//String in long umwandeln sl->Strings[i+1]=IntToStr(DataSetValueLong+AddDataSetValueLong);//Addieren und zuweisen SchonDa=true; } } if (SchonDa==false){ sl->Add(DataSetName); sl->Add(AddDataSetValue); } sl->SaveToFile(MDB::MDBPath+MDB::MDBFile); delete sl; } } //--------------------------------------------------------------------------- void MDB::SetAllDsValuesToZero() { if (FileExists(MDB::MDBPath+MDB::MDBFile)){ //Wenn DB vorhanden TStringList *sl=new TStringList(); //Alle Ds Values Auf Null (0) setzen sl->LoadFromFile(MDB::MDBPath+MDB::MDBFile);// for (long i=0;i<sl->Count;i=i+2){ sl->Strings[i+1]=IntToStr(0); } sl->SaveToFile(MDB::MDBPath+MDB::MDBFile); delete sl; } }
Screen_Capture_Class.cpp:
// Screen_Capture_Class (c) 2009 by Longo //********************************************************************* #include <Forms.hpp> #include <ExtCtrls.hpp> #include <JPEG.hpp> #include <vcl.h> class ScreenCaptureClass { public: TJPEGImage *jpg; // Buffer fürs Bild AnsiString SpeicherPfad; // in diesen Ordner werden die Bilder gespeichert long PicCount; // Interner Bilderzähler (wird durch SavePic +1) long PicCountOld; // Hilfsvariable zum errechnen der aktuellen Anzahl bool isRecording; // Aufnamemodus ( An/Aus ) int Toggle; // Toggle-Variable (z.B. für blinken während der // Aufnahme), wechselt bei SavePic zwischen 0 und 1. void ScreenCap(TImage *MyImage, int CapX, int CapY, int CapWidth, int CapHeight); void SavePic(TImage *MyImage, AnsiString DateiName); void DeletePic(AnsiString DateiName, long PicNr); }; //--------------------------------------------------------------------------- //Methoden: //--------------------------------------------------------------------------- // Screen-Ausschnitt in ein TImage capturen: void ScreenCaptureClass::ScreenCap(TImage *MyImage, int CapX, int CapY, int CapWidth, int CapHeight) { HDC HScreenDC = GetDC(NULL); // NULL für Screen statt Fenster MyImage->Picture->Bitmap->Handle = CreateCompatibleBitmap(HScreenDC, CapWidth,CapHeight); int result = GetDeviceCaps(HScreenDC, RASTERCAPS); if (result & RC_PALETTE) { int palette_size = GetDeviceCaps(HScreenDC, SIZEPALETTE); if (palette_size == 256) { const size_t size = sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY); unsigned char* pBuffer = new unsigned char[size]; LPLOGPALETTE lplogpal = reinterpret_cast<LPLOGPALETTE>(pBuffer); lplogpal->palVersion = 0x300; lplogpal->palNumEntries = 256; GetSystemPaletteEntries(HScreenDC, 0, 256, lplogpal->palPalEntry); MyImage->Picture->Bitmap->Palette = CreatePalette(lplogpal); delete [] pBuffer; } } BitBlt(MyImage->Picture->Bitmap->Canvas->Handle, 0, 0, CapWidth, CapHeight,HScreenDC, CapX, CapY,SRCCOPY); ReleaseDC(0, HScreenDC); } //--------------------------------------------------------------------------- // TImage speichern (dem Dateinamen wird "00000001.jpg" u.s.w. angehangen void ScreenCaptureClass::SavePic(TImage *MyImage, AnsiString DateiName) { jpg = new TJPEGImage(); jpg->Assign(MyImage->Picture->Graphic); jpg->CompressionQuality = 75; jpg->Compress(); jpg->SaveToFile(ScreenCaptureClass::SpeicherPfad+DateiName+"_" +FormatFloat("00000000",ScreenCaptureClass::PicCount)+".jpg"); delete jpg; // internen Bilderzähler um 1 erhöhen: ScreenCaptureClass::PicCount=ScreenCaptureClass::PicCount+1; // internes Toggle setzen (z.B. für blinken während der Aufnahme): ScreenCaptureClass::Toggle=1-ScreenCaptureClass::Toggle; } //--------------------------------------------------------------------------- void ScreenCaptureClass::DeletePic(AnsiString DateiName, long PicNr) { // Bild löschen (PicNr gibt die Nummer des Bildes an) DeleteFile(ScreenCaptureClass::SpeicherPfad+DateiName+"_"+FormatFloat("00000000",PicNr)+".jpg"); }
Wen es interessiert:
Das Programm ist zum Speichern von Ausschnitten auf dem Screen (Screencapture).
Spezialisiert auf eine bestimmte Internetseite (o)Y(o)
(Die Seite ist aus dem Code zu erkennen)Hier könnt ihr das gesamte Projekt als .rar Datei bei rapidshare runterladen:
http://rapidshare.com/files/246743761/MyFreeCamCap.rar.htmlVielen Dank fürs lesen (und antworten)
Longops: das mit dem Copyright (c) 2009 by Longo ist nur ein Witz
-
Hallo
Zunächsteinmal ist Programmierstil Geschmackssache, du wirst also hier keine einstimmige Meinung bekommen. Was mir auffällt :
- An einigen Stellen nicht einheitlicher Einrückstil
- Klassendeklaration und -implementation in einer cpp-Datei? Headerdateien verwenden!
- bei Funktionen ohne Parameter das void in den Klammern weglassen : alter C-Stil
- in deinem Beispiel vielleicht noch nicht relevant, aber du solltest dir const-correctness anschauen und einsetzen.bis bald
akari
-
akari schrieb:
- An einigen Stellen nicht einheitlicher Einrückstil
Was heißt 'nicht einheitlich', an vielen Stellen wurde gar nicht eingerückt. Das ist dann auch nicht mehr Geschmackssache, sondern absoluter Verlust von Struktur und Übersicht.
-
_matze schrieb:
Was heißt 'nicht einheitlich', an vielen Stellen wurde gar nicht eingerückt.
In seiner IDE mag das vllt. noch richtig eingerückt sein.
Aber wenn man den Code ins Forum-Nachrichtenfeld kopiert, gehen tätsächlich etliche Formatierungen verloren.Nur,
wenn er schon eine Meinung von der Community abgefragt, sollte er sich vorher seinen Beitrag in der Vorschau ansehen.
Ich drück da mal ein Auge zumfg
kpeter
-
Aus diesem Grund sollte man ja auch "Einzug mit Tabs", "Füllen mit Tabs" usw in den Editor-Optionen abschalten.
-
witte schrieb:
Aus diesem Grund sollte man ja auch "Einzug mit Tabs", "Füllen mit Tabs" usw in den Editor-Optionen abschalten.
nee, das alles anschalten, und nicht mit leerzeichen einrücken.
-
witte schrieb:
Aus diesem Grund sollte man ja auch "Einzug mit Tabs", "Füllen mit Tabs" usw in den Editor-Optionen abschalten.
+1.
-
Also wenn ich hier Code poste, dann wird eigentlich immer korrekt eingerückt. Die Ausnahme bildet natürlich eine blöde Mischung aus Tabs und Leerzeichen (z.B. 2 Leerzeichen eingerückt, Tabweite hier aber 4). Das wird hier aber nicht (nur) der Fall sein, da teilweise auch auf der niedrigsten Ebene nicht eingerückt wurde.
-
Sieht eigentlich ganz ordentlich aus
Mir sind nur ein paar Kleinigkeiten aufgefallen:- Verwendung von signed Datentypen statt unsigned:
Es macht keinen Sinn, signed integer für die Höhe und Breite eines Bildes zu benutzen, da ein Bild niemals eine negative Höhe oder Brite haben kann. Da die VCL aber selbst darauf keine Rücksicht nimmt vermeidet man durch die verwendung von signed integer einige Compilerwarnungen und daher ist es ok. Grundsätzlich sollte man aber die passenden Typen benutzen.- Übergabe von konstanten Parametern als const Referenzen
Bei der Übergabe von AnsiStrings übergibst du sie per value, obwohl sie im Funktionsrumpf nicht verändert werden. Du solltest in diesem Fall eine Übergabe per const Referenz vorziehen.- Benutzung von new[]/delete[] in einem Anweisungsblock
Dank der STL und smart pointer benötigt man nur noch selten new[]/delete[], stattdessen kannst du besser einen std::vector benutzen und ihn mit resize() auf die richtige Grösse setzen. Damit wird dein Code robuster, da std::vector bei seiner Zerstörung den allokierten Speicher immer wieder freigibt. Bei new[]/delete[] kann man mal den delete[] Aufruf vergessen oder, wenn z.B. eine Exception auftritt, der entsprechende delete[] Aufruf nie gemacht wird.
Für temporäre VCL Objekte gilt das Gleiche, wenn man sie in einen std::auto_ptr verpackt sind sie exception safe. Besser sind jedoch die smart_ptr der boost Bibliothek oder TR1, da auto_ptr einige unangenehme Eigenschaften hat, die für temporäre Objekte jedoch keine Rolle spielen.- const correctness
Wenn du ernsthaft programmieren willst solltest du dir das Konzept unbedingt anschauen. Leider ist die VCL nicht const correct und auch der Builder (zumindest BCB 6) hat damit einige Probleme, sodass sich die const correctness im Zusammenhang mit der VCL nicht immer umsetzen lässt.PS:
Die Vollqualifizierung der MDB member irritiert mich ein wenig, ich habe am Anfang gelaubt, das seien statische member. Gibt´s einen Grund für diese Schreibweise?
-
Hallo, nach langer Zeit...
Ich danke euch allen herzlich für die Anregungen.
Da werde ich doch ein paar Veränderungen vornehmen müssen...
Das mit dem Einrücken ist eine Frage der Disziplin (die mir wohl noch fehlt).
Wenn möglich const (const correctness) zu benutzen ist mir jetzt auch klar.Damit wird dein Code robuster, da std::vector bei seiner Zerstörung den allokierten Speicher immer wieder freigibt
davor hab ich mich bis jetzt gescheut (warum auch immer), werds mir aber aneignen.
Die Vollqualifizierung der MDB member irritiert mich ein wenig, ich habe am Anfang gelaubt, das seien statische member. Gibt´s einen Grund für diese Schreibweise?
Ich bin mir nicht sicher was du meinst. Das?
MDB::MDBPath+MDB::MDBFile
Im übrigen möchte ich mich mich für meine späte Antwort entschuldigen.
Aber ich hatte die letzten Wochen einfach keine Zeit für mein Hobby.Danke nochmal herzlichst an alle!
Longo