Dynamische und statische Speicherbeschaffung, Klassen-Templates



  • Danke für die Antworten, ich hätte da noch ein paar Rückfragen

    Shade Of Mine schrieb:

    In diesem Spezialfall, ja.

    Aber was wenn du:

    int i;
    cin>>i;
    int* p=new int[i];
    

    schreibst?

    Ok, das verstehe ich. Wobei ich mich aber frage, warum genau in diesem Fall nicht auch statisch geht. Ist es deshalb, weil der übersetzte Code "dicht" liegen muss, und dass Array dann möglicherweise nicht mehr reinpasst, wenn es zu gross ist?

    Shade Of Mine schrieb:

    Selbes Beispiel wie oben:

    int i;
    cin>>i;
    Ding<char, i> einDing; //geht nicht
    Ding<char> einDing(i); //geht sehr wohl
    

    Warum geht Ersteres pauschal nicht? Wenn i zum Beispiel nicht bestimmend für die Grösse eines Arrays oder ähnlichem ist, dann weiß der Compiler doch sowieso immer, wie gross daraus erzeugte Objekte werden.

    Skym0sh0 schrieb:

    Kurzum, bei Templates muss der Compiler komplett Bescheid wissen, wie das Ergebnis auszusehen hat, er passt lediglich die Typen an. (so ausgedrückt stimmt das zwar nicht, aber das ist mir jetzt egal, denn angepasst wird nix) Zur Not wendet er Rekursion an um sich sämtliche Definitionen herzuleiten. (Bis zum Ende ist für den Compiler nämlich nicht alles bekannt)

    Heißt dass, das z.B bei Template-Funktionen der Compiler schon im voraus alles berechnet und durchgeht?

    Skym0sh0 schrieb:

    Gibst du aber Variablen oder Werte (Magic Values) an Funktionen, dann weiss der Compiler ja nicht was dort passiert. Und ausführen tut er das auch nicht (wie bei Templates), erst zur Laufzeit wird sowas dann berechnet.

    Speziell bei Konstruktoren muss er aber doch trotzdem irgendwie schon im voraus berechnen, wie gross die zu erwartenden Objekte werden.



  • climits schrieb:

    Shade Of Mine schrieb:

    In diesem Spezialfall, ja.

    Aber was wenn du:

    int i;
    cin>>i;
    int* p=new int[i];
    

    schreibst?

    Ok, das verstehe ich. Wobei ich mich aber frage, warum genau in diesem Fall nicht auch statisch geht. Ist es deshalb, weil der übersetzte Code "dicht" liegen muss, und dass Array dann möglicherweise nicht mehr reinpasst, wenn es zu gross ist?

    wie will denn der Compiler wissen, was du zur Laufzeit von der Standard-Eingabe liest?

    climits schrieb:

    Shade Of Mine schrieb:

    Selbes Beispiel wie oben:

    int i;
    cin>>i;
    Ding<char, i> einDing; //geht nicht
    Ding<char> einDing(i); //geht sehr wohl
    

    Warum geht Ersteres pauschal nicht? Wenn i zum Beispiel nicht bestimmend für die Grösse eines Arrays oder ähnlichem ist, dann weiß der Compiler doch sowieso immer, wie gross daraus erzeugte Objekte werden.

    Template-Paramater müssen zur Compilezeit ausgewertet werden können. Darum geht das stumpf nicht.



  • climits schrieb:

    Shade Of Mine schrieb:

    Selbes Beispiel wie oben:

    int i;
    cin>>i;
    Ding<char, i> einDing; //geht nicht
    Ding<char> einDing(i); //geht sehr wohl
    

    Warum geht Ersteres pauschal nicht? Wenn i zum Beispiel nicht bestimmend für die Grösse eines Arrays oder ähnlichem ist, dann weiß der Compiler doch sowieso immer, wie gross daraus erzeugte Objekte werden.

    Und woher weiss er, ob diese Zahl nicht doch die Größe eines Arrays ist?

    climits schrieb:

    Skym0sh0 schrieb:

    Kurzum, bei Templates muss der Compiler komplett Bescheid wissen, wie das Ergebnis auszusehen hat, er passt lediglich die Typen an. (so ausgedrückt stimmt das zwar nicht, aber das ist mir jetzt egal, denn angepasst wird nix) Zur Not wendet er Rekursion an um sich sämtliche Definitionen herzuleiten. (Bis zum Ende ist für den Compiler nämlich nicht alles bekannt)

    Heißt dass, das z.B bei Template-Funktionen der Compiler schon im voraus alles berechnet und durchgeht?

    Ja, der Compiler löst sämtliche Templates auf. Normale Funktion werden erst zur Laufzeit ausgeführt, da liegt der Unterschied.

    climits schrieb:

    Skym0sh0 schrieb:

    Gibst du aber Variablen oder Werte (Magic Values) an Funktionen, dann weiss der Compiler ja nicht was dort passiert. Und ausführen tut er das auch nicht (wie bei Templates), erst zur Laufzeit wird sowas dann berechnet.

    Speziell bei Konstruktoren muss er aber doch trotzdem irgendwie schon im voraus berechnen, wie gross die zu erwartenden Objekte werden.

    Die Objekte die er erzeugt sind abhängig von der Klassendefinition, und nur davon. Als einfache Merkrichtlinie kannst du dir merken, jedes Objekt einer Klasse ist so gross wie alle Membervariablen zusammenaddiert. (Wobei Arrays meistens eher nur eine Zeiger sind und damit nur 4/8 Byte groß --> selbst fette std::string geben bei sizeof nur 14 oder so wieder [MSVC2010, aber ohne Gewähr])



  • climits schrieb:

    Ok, das verstehe ich. Wobei ich mich aber frage, warum genau in diesem Fall nicht auch statisch geht. Ist es deshalb, weil der übersetzte Code "dicht" liegen muss, und dass Array dann möglicherweise nicht mehr reinpasst, wenn es zu gross ist?

    Naja, stell dir vor ich kann Zahlen von 1 bis 4 Milliarden eingeben.
    Soll der Compiler jetzt jedesmal 4 Milliarden ints reservieren, nur weil die theoretische Möglichkeit besteht, dass ein User einmal 4 Milliarden eingeben könnte?

    Warum geht Ersteres pauschal nicht? Wenn i zum Beispiel nicht bestimmend für die Grösse eines Arrays oder ähnlichem ist, dann weiß der Compiler doch sowieso immer, wie gross daraus erzeugte Objekte werden.

    Das ist eine Anforderung der Sprache.
    Template Parameter gehören zum Typ.
    Foo<3> ist ein anderer Typ als Foo<4>.
    Und Typen müssen zur Compilezeit feststehen.

    Bedenke folgendes:

    template<int I> class Foo;
    template<> class Foo<3> { int arr[1000000]; };
    template<> class Foo<4> { int arr[1]; };
    

    Foo<3> ist jetzt 1000000*sizeof(int) groß, Foo<4> ist 1*sizeof(int) groß und Foo<5> gibt es garnicht.

    Was soll hier passieren:

    int i;
    cin>>i;
    Foo<i> bar;
    

    wenn ich 5 eingebe. Foo<5> existiert ja nicht...

    Heißt dass, das z.B bei Template-Funktionen der Compiler schon im voraus alles berechnet und durchgeht?

    Jein.
    Teilweise ja.
    Man kann viele Sachen per Templates direkt während dem Kompilieren berechnen.

    Beispiel:

    template<int A, int B>
    class Calc {
    public:
       static int result=A+B;
    };
    cout<<Calc<3,4>::result;
    

    Hier wird der Compiler direkt 7 ausgeben. Das kann natürlich viel komplexer werden - mit Rekursion und komplexeren Operationen.

    Aber es bedeutet nicht dass Templates automagisch alles zur Compiletime berechnen.

    Speziell bei Konstruktoren muss er aber doch trotzdem irgendwie schon im voraus berechnen, wie gross die zu erwartenden Objekte werden.

    Nein.
    Bring mal ein Beispiel wo du denkst, dass er das muss.



  • climits schrieb:

    Shade Of Mine schrieb:

    In diesem Spezialfall, ja.

    Aber was wenn du:

    int i;
    cin>>i;
    int* p=new int[i];
    

    schreibst?

    Ok, das verstehe ich. Wobei ich mich aber frage, warum genau in diesem Fall nicht auch statisch geht. Ist es deshalb, weil der übersetzte Code "dicht" liegen muss, und dass Array dann möglicherweise nicht mehr reinpasst, wenn es zu gross ist?

    Der gcc hat eine Erweiterung dafür, das geht prima.
    Das Array kann "dicht" auf den Stack gepackt werden, auch mehrere, da muss man nicht jede mögliche Größe reservieren. Das Risiko des Stackoverflows gibt es auch so.
    Es ist allerdings nicht mit dem Zero-Overhead-Prinzip vereinbar, da sich der Compiler extra die Größe merken muss. Bei n Arrays muss er wohl n-1 Größen Speichern und die Zugriffe werden schwieriger, weil die Größen addiert werden oder ebenfalls gespeichert werden müssen.



  • Ok danke, das meiste hab ich denke ich verstanden.

    Shade Of Mine schrieb:

    Nein.
    Bring mal ein Beispiel wo du denkst, dass er das muss.

    Wenn ich eine Klasse mit einem Array als member habe das statisch erzeugt werden soll, und einen Konstruktor mit dem dann per Parameter wieder die Grösse des Arrays bestimmt werden soll:

    class Ding {
    Ding(int k);
    int i;
    int arr[i];
    };
    Ding::Ding(int k) 
    : i(k) {...}
    

  • Mod

    Du argumentierst mit einer nicht-standardkonformen Erweiterung eines speziellen Compilers, der ein C99-Feature umsetzt, dessen Existenz selbst viele C-Fans aufgrund vieler Probleme am liebsten totschweigen?



  • Und in diesem Fall:

    class Foo
    {
    	int i;
    	int arr[i];
    
    public:
    	Foo(int x)
    		: i(x)
    	{}
    };
    
    int main()
    {
    	int size;
    	cin >> size;
    
    	Foo f(size);
    
    	return 0;
    }
    

    ?


  • Mod

    Error: ‘i’ undeclared here (not in a function) (C99)
    oder
    Error: invalid use of non-static data member ‘Foo::i’ (C++)



  • mm, okay, das war wohl nichts. Aber immerhin noch was dazugelernt 😃
    Hab jetzt glaube ich alles begriffen. Danke für alle Antworten.



  • Wenn nicht frag nach...



  • Variablen die nicht statisch sind müssen immer mit dem Konstruktor initialisiert werden, deshalb die Fehlermeldung. Und ein Array kann man scheins nur durch Angabe der Grösse deklarieren (und damit wahrscheinlich auch definieren), weil das wahrscheinlich auch zum Typ gehört, der schon zur Kompilierzeit feststehen muss. Die Grösse von Objekten der Klasse steht damit von vorneherein fest.


  • Mod

    Und ein Array kann man scheins nur durch Angabe der Grösse deklarieren (und damit wahrscheinlich auch definieren), weil das wahrscheinlich auch zum Typ gehört, der schon zur Kompilierzeit feststehen muss. Die Grösse von Objekten der Klasse steht damit von vorneherein fest.

    So ist es.

    Variablen die nicht statisch sind müssen immer mit dem Konstruktor initialisiert werden, deshalb die Fehlermeldung.

    😕



  • SeppJ schrieb:

    😕

    Also ich mein, sie müssen nicht unbedingt mit dem Konstruktor initialisiert werden, aber sie dürfen nicht innerhalb der Klassendefinition initialisiert werden, also z.B:

    class A {
    int i = 3;
    int k = i;
    };
    

  • Mod

    Jain. Im Prinzip hast du Recht. Jedoch wurde dein Beispiel

    class A {
    int i = 3;
    int k = i;
    };
    

    als Syntaxerweiterung in C++11 eingeführt und macht das, was man intuitiv erwarten würde. Das ist jedoch eher eine schicke Art und Weise, einen Konstruktor zu schreiben. Der Code wird aber natürlich erst bei der tatsächlichen Erzeugung eines Objektes ausgeführt.



  • In C++ 2011 geht das also? Scheinbar muss ich mal mein Code::Blocks aktualisieren.



  • climits schrieb:

    Shade Of Mine schrieb:

    Selbes Beispiel wie oben:

    int i;
    cin>>i;
    Ding<char, i> einDing; //geht nicht
    Ding<char> einDing(i); //geht sehr wohl
    

    Warum geht Ersteres pauschal nicht? Wenn i zum Beispiel nicht bestimmend für die Grösse eines Arrays oder ähnlichem ist, dann weiß der Compiler doch sowieso immer, wie gross daraus erzeugte Objekte werden.

    "i" kann in C++ "bestimmend" für alles mögliche sein. Es kann z.B. sein dass Ding<char, 1> eine Funktion Foo hat (also Ding<char, 1>::Foo existiert), Ding<char, 0> aber nicht (also Ding<char, 0>::Foo existiert nicht).
    Weiters sind Ding<char, 1> und Ding<char, 0> vollkommen unverwandte Typen. D.h. es gibt gar keine Möglichkeit in C++ über ein Ding<char, wurstWasHierSteht> zu sprechen -- abgesehen natürlich über weitere Templates.

    Nur da Templates in C++ nicht zur Laufzeit ausgewertet werden können beisst sich der Schwanz hier in den Hund.

    Es gibt allerdings eine Sprachgruppe die in dieser Richtung weit mehr erlaubt als C++, und das sind die funktionalen Sprachen.

    C++ geht das ganze einfach nach dem Motto an "wir wollen statisch effizienten Code erzeugen können" an, und verbietet daher alles (viel) was das verhindern würde. Und das schliesst die dynamische Auswertung von Templates mit ein.

    Viele funktionale Sprachen gehen die Sache nach dem Motto "wir wollen dass möglichst viel geht" an, und nehmen dafür in Kauf dass man ziemlich komplexe Interpreter/JIT Kombinationen, üblicherweise gepaar mit nem Garbage-Collector + schlauen Caching-Algorithmus braucht um sie in annehmbarer Zeit ausführen zu können.


Anmelden zum Antworten