Eine Stilfrage



  • ich hab da grad was blödes:

    class Console{
    	private:
    	Size sizeX,sizeY;
    	CHAR_INFO* bufferBegin,bufferEnd;
    	CHAR_INFO* lineBegin,lineEnd;
    	CHAR_INFO* pos;
    	public:
    	Console(){
    		HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
    		CONSOLE_SCREEN_BUFFER_INFO csbi;
    		GetConsoleScreenBufferInfo(hConsole,&csbi);
    		sizeX=csbi.dwSize.X;
    		sizeY=csbi.dwSize.Y;
    		bufferBegin=new CHAR_INFO[sizeY*sizeX];
    		bufferEnd=bufferBegin+sizeY*sizeX;
    		lineBegin=bufferBegin;
    		lineEnd=lineBegin+sizeX;
    		pos=lineBegin;
    		//TODO: aktuellen consoleninhalt in den buffer kopieren
    		//cursorposition aus csbi lesen und pos und line* danach 
    		//setzen
    	}
    	void carriageReturn(){
    		pos=lineBegin;
    	}
    	void newLine(){
    		if(lineEnd==bufferEnd){
    			lineBegin=bufferBegin;
    			lineEnd=lineBegin+sizeX;
    			pos-=(sizeY*sizeX-sizeX);
    		}
    		else{
    			lineEnd+=sizeX;
    			lineBegin=lineEnd;
    			pos+=sizeX;
    		}
    		for(CHAR_INFO* p=lineBegin;p!=lineEnd;++p){
    			*p->UnicodeChar=' ';
    			*p->Attributes=7;
    		}
    	}
    	void carriageReturnNewLine(){
    		carriageReturn();
    		newLine();
    	}
    	void writeSpecialChar(char ch){
    	}
    	void writePrintableChar(char ch){
    		*pos=ch;
    		++pos;
    		if(pos==lineEnd)
    			newLine();
    	}
    	void put(char ch){
    		if(ch<16)
    			writeSpecialChar(ch);
    		else
    			writePrintableChar(ch);
    	}
    };
    

    ist alles noch pseudocode, ich experimentiere, wie sich code für eine console anfühlen sollte.

    aber wie mach ich den konstruktor so richtig initialisiererlistenlastig? mir scheint, das würde der ganzen angelegenheit die beine brechen.



  • volkard schrieb:

    ich hab da grad was blödes:
    ...
    aber wie mach ich den konstruktor so richtig initialisiererlistenlastig? mir scheint, das würde der ganzen angelegenheit die beine brechen.

    C-Code in C++ zu wandeln ist nun einmal nicht immer sinnvoll möglich.

    Normalerweise initialisiere ich bei ähnlichen Fällen (aber bei einzelnen Werten) über Rückgabewerte von statische Methoden, da die Initialisierung in so einen Fall eh schon ein Thema für sich ist. Bzw. Kapsel dies in eigene RAII-Objekte.

    Ja, eine einfache Lösung fällt mir hier nicht ein, aber ich muss gestehen das ich zu 95+% mit C++ Schnittstellen hantiere (oder mit Wrappern um eben solche).

    cu André


  • Administrator

    @volkard,
    Kann es überhaupt mehrere Objekt von Console geben? Wäre das nicht perfekt für ein Singleton mit Factory Pattern. In der Fabrikmethode holst du dir ein CONSOLE_SCREEN_BUFFER_INFO und übergibst dieses an den Konstruktor von Console . Dann kann alles in der Initialisierungsliste ausgeführt werden. Wobei man natürlich die Reihenfolge der Initialisierung beachten muss.

    Allerdings lagere ich eigentlich immer gerne Speicherverwaltungsaufgaben aus der Initialisierungsliste raus. Also ein new erfolgt bei mir immer im Konstruktorrumpf und nicht in der Initialisierunsliste.

    Grüssli



  • Dravere schrieb:

    @volkard,
    Kann es überhaupt mehrere Objekt von Console geben?

    bevor deine factory im spiel war, gab es keinen grund dagegen. vielleicht ist es ganz angenehm, ein zweites konsolenfenster für debug-ausgaben oder cerr aufzumachen.

    Wäre das nicht perfekt für ein Singleton mit Factory Pattern. In der Fabrikmethode holst du dir ein CONSOLE_SCREEN_BUFFER_INFO und übergibst dieses an den Konstruktor von Console . Dann kann alles in der Initialisierungsliste ausgeführt werden. Wobei man natürlich die Reihenfolge der Initialisierung beachten muss.

    wenn das nur geschieht, um die initialisiererliste benutzen zu können, ist das nicht ok, finde ich.

    Allerdings lagere ich eigentlich immer gerne Speicherverwaltungsaufgaben aus der Initialisierungsliste raus. Also ein new erfolgt bei mir immer im Konstruktorrumpf und nicht in der Initialisierunsliste.

    jo, geht mir auch so.


  • Administrator

    volkard schrieb:

    wenn das nur geschieht, um die initialisiererliste benutzen zu können, ist das nicht ok, finde ich.

    Naja, die Frage ist eher, ob der Code oberhalb der Zeile 12 im Konstruktor wirklich zu Console gehören sollte. Ist das die Aufgabe vom Konstruktor von Console?
    Wie du selber gesagt hast, wenn jemand die Konsole über STD_ERROR_HANDLE laufen lassen möchte, wäre es besser, wenn der Konstruktor gleich ein gültiges CONSOLE_SCREEN_BUFFER_INFO Objekt erwarten würde.

    Das erstellen eines gültigen CONSOLE_SCREEN_BUFFER_INFO Objektes ist meiner Meinung nach einfach nicht die Aufgabe des Konstruktors von Console .

    Grüssli



  • Dravere schrieb:

    volkard schrieb:

    wenn das nur geschieht, um die initialisiererliste benutzen zu können, ist das nicht ok, finde ich.

    Naja, die Frage ist eher, ob der Code oberhalb der Zeile 12 im Konstruktor wirklich zu Console gehören sollte. Ist das die Aufgabe vom Konstruktor von Console?
    Wie du selber gesagt hast, wenn jemand die Konsole über STD_ERROR_HANDLE laufen lassen möchte, wäre es besser, wenn der Konstruktor gleich ein gültiges CONSOLE_SCREEN_BUFFER_INFO Objekt erwarten würde.

    Das erstellen eines gültigen CONSOLE_SCREEN_BUFFER_INFO Objektes ist meiner Meinung nach einfach nicht die Aufgabe des Konstruktors von Console .

    Grüssli

    ich darf keine lokalen variablen anlegen. hmm.
    die frage ist also, ob innerhalb eines konstruktors gerechnet werden darf!

    da führt mich doch sofort zu

    HashTable::HashTable(size_t size)
    :keys(nextPrimeTwin(size/4*5))
    ,values(nextPrimeTwin(size/4*5))
    {
    }
    

    das ist eine hashtable, deren interne größe ein primzahlenzwilling sein soll, weil das recht gute aussichten auf kollissionsarmut bietet. außerdem sollen keys und values in getrennten speicherbereichen liegen, weil die keys sehr klein und die values sehr lahm sind. bei <=80% füllstand habe ich beste chancen, unter zwei key-zugriffen zu bleiben, und bei tollen keys habe ich beste aussichten, bei ungefähr einem value-zugriff zu bleiben. außerdem ist die berechnung des nächsten primzahlenzwillings eine sehr teure angelegenheit. die members keys und values sind vector-ähnliche klassen, die im konstuktor ihr größe haben wollen. ich möchte keine andere hashtable-struktur wählen nur weil diese hier mich bei den initialisiererlisten verwirrt.

    obiger code ist suboptimal. weg damit.

    HashTable::HashTable(size_t size)
    {
       size_t s=nextPrimeTwin(size/4*5);
       keys=Array<KEY>(s);
       values=Array<VALUE>(s);
    }
    

    naja, auch kacke.

    den benutzer zu zwingen,

    HashTable<...,...> h(nextPrimeTwin(10000));
    

    zu benutzen, fällt auch in die kategogie M.I.S.T..

    also

    HashTable::HashTable(size_t size)
    :realHashTable(nextPrimeTwin(size/4*5))
    {
    }
    

    naja, bedenklich, nur zu diesem zweck ne klasse aufzumachen.

    class PrimeTwin{
    ...
       PrimeTwin(size_t n)
       size_t getValue()
    ...
    HashTable::HashTable(PrimeTwin size)
    :keys(size.getValue())
    ,values(size.getValue())
    

    ich glaub', ich steh im wald.

    das ist anscheinend kein problem, das mit ordentlichem design weggemacht werden kann. es gibt keinen königsweg. sonst hätte schon längst einer eine sprache erfunden, die alles richtig macht, und sie nach einer insel benannt, fürchte ich.

    das konzept mit den initialisiererlisten ist ein wenig fürn popo. warum soll ich nicht die memberkonstruktoren aufrufen dürfen, wann ich mag? es würde doch reichen, daß ich bis zum ende meineskostruktors alle aufgerufen haben muß. und wenn ich die rehenfolge von der deklarationsreihenfolge abweichen lasse, muß ich halt drauf achten, daß das auch mit dem destruktor kompatibel ist, was fast immer der fall ist, weil die members sich gar nicht gegenseitig kennen.



  • volkard schrieb:

    warum soll ich nicht die memberkonstruktoren aufrufen dürfen, wann ich mag?

    Der Grund wird gewesen sein, daß man ein Objekt dann im Konstruktor-Body nicht als ein vollständig initialisiertes Objekt behandeln hätte können; so etwas wie

    void doSomethingWith (MyValueTypeClass& mvtc);
    MyValueTypeClass::MyValueTypeClass (void)
    {
        ...
        doSomethingWith (mvtc);
        ...
    }
    

    wäre dann höchst volatil.

    Tatsächlich ist das natürlich selbst mit Initialisierungslisten nicht vollständig umsetzbar (-> virtuelle Funktionen, typeid, dynamic_cast<>). Initialisierungslisten sind der Versuch eines Workarounds für ein größeres Designproblem: nämlich, daß jede Klasse standardmäßig Wertetypensemantik hat.


Anmelden zum Antworten