mehrere Konstruktoren

Hallo!
Ich habe mich jetzt durch die diversen Beiträge hier gelesen, aber nichts gefunden, mit dem ich der Lösung meines Problems näher kommen könnte, deshalb ein neues Topic.

Ich habe eine recht einfache Klasse, der ich 2 Konstruktoren Meine_Teimer und Meine_Timer(int), aber bei der Deklaration habe ich Probleme am DUE, nicht aber am PC.

Initialisierung soll natürlich vor setup() und loop() erfolgen mit:

Meine_Timer ein_timer;
Meine_Timer zehn_timer(10);

Der Header sieht so aus:

//Test.h
class Meine_Timer {
		public:
			Meine_Timer();
			Meine_Timer(int i_timer_anzahl);
			int init(int i_timer_anzahl); //soll weg
			int set(long l_timer_zeitspanne);
			int set(int i_timer_nummer, long l_timer_zeitspanne);
			//... usw, einige Methoden,
			//eine Struktur für die Start- und Ablaufzeit und Timer läuft
			//sowie Der Zeiger auf die Struktur und die Strukturlänge
	};
};

Folgende Konstruktoren "funktionieren" am PC mit DEV++ aber nicht am Arduino:

//Test.c - das geht nicht
include "Test.h"
Meine_Timer::Meine_Timer(){
	Meine_Timer(1);
}

Meine_Timer::Meine_Timer(int i_timer_anzahl){
	Meine_Timer::stru_timer * p_stru;  //Zeiger auf die Struktur, wird auch in der Klasse abgelegt
	p_stru = new stru_timer[i_timer_anzahl];
	p_struct = p_stru;  //p_struct = Zeiger in der Struct der Klasse
//hier dann der Rest der Initialisation
}

Folgende Konstruktoren "funktionieren" zwar auch am Arduino, ist aber nicht schön, weil ich hier eine "Hiilfsmethode" verwende, die ja auch im im Header definiert sein muss, und das sollte eigentlich nicht sein.

//Test.c - so geht es
include "Test.h"
Meine_Timer::Meine_Timer(){
	init(1);
}

Meine_Timer::Meine_Timer(int i_timer_anzahl){
	init(i_timer_anzahl);
}

int init(int i_timer_anzahl){	
	Meine_Timer::stru_timer * p_stru;  //Zeiger auf die Struktur, wird auch in der Klasse abgelegt
	p_stru = new stru_timer[i_timer_anzahl];
	p_struct = p_stru;  //p_struct = Zeiger in der Struct der Klasse
//hier dann der Rest der Initialisation
}

Nachdem ich vor etwa 15 Jahren von C++ weg bin und nun durch die "kleinen Arduino-Racker" wieder Fuß fassen will bin ich mir nicht sicher, ob ich nicht irgendwo einen Kardinal-Fehler in meinen Gedanken habe.

Im Konstruktor Meine_Timer::Meine_Timer(int i_timer_anzahl) wird per new Speicher angefordert und der Zeiger darauf auch in der Klase abgelegt. Und das scheint das Problem zu sein. Ein check des Zeigers auf die Struktur ergibt, dass ein Null-Pointer generiert wird, das new also anscheinend fehl schlägt.

Meine Frage: Gibt es eine Erklärung, warum Variante 2 meiner Test.cpp einwandfrei läuft, Variante 1 allerdings keinen Speicher allokiert?

Danke schon mal für Eure Hilfe :slight_smile:
Wolfgang

Ja, C++ wurde erfunden, als OOP noch in den Kinderschuhen war.....

Weg 3 ist der richtige!!!

Alternativ:
Auf den Meine_Timer() Konstruktor verzichten und stattdessen Meine_Timer(int i_timer_anzahl=1) deklarieren.

Also die Anzahl mit einem Standard Wert vorbelegen.
Von mir aus auch mit null bei nicht verwendung

Im Konstruktor Meine_Timer::Meine_Timer(int i_timer_anzahl) wird per new Speicher angefordert

Das ist auf µC eigentlich eine dumme Idee. (sorry)
Wenn es irgend möglich ist, verzichte auf dynamische Speicherverwaltung.
Ich würde fast sagen: Um jeden Preis!

Vermutlich sind Templates hier das Mittel der Wahl.

combie:
Ja, C++ wurde erfunden, als OOP noch in den Kinderschuhen war.....

nana, zu "meiner Zeit" gab es schon recht vernünftiges OOP....

Weg 3 ist der richtige!!!

Alternativ:
Auf den Meine_Timer() Konstruktor verzichten und stattdessen Meine_Timer(int i_timer_anzahl=1) deklarieren.

Also die Anzahl mit einem Standard Wert vorbelegen.
Von mir aus auch mit null bei nicht verwendung

Genau das will ich nicht ,weil dann sind wir wieder beim Standard C....
Es sollen damit einfach und schnell (ohne auf irgendwelche Parameter achten zu müssen) einfache Timer und z.B. kaskadierte Timer (mit Angabe der Timer-Anzahl) erstellt werden können. Mein Projekt verwendet im Endausbau an die 40 Timer, davon 35 kaskadiert bis zu 16 Ebenen. Die Klasse erspart mir da sehr viel Zeit beim coden, wenn meine Idee mit der Klasse, die mir beim 3. Timer, den ich einsetzen wollte, kam, funktioniert!

Das ist auf µC eigentlich eine dumme Idee. (sorry)
Wenn es irgend möglich ist, verzichte auf dynamische Speicherverwaltung.
Ich würde fast sagen: Um jeden Preis!

Vermutlich sind Templates hier das Mittel der Wahl.

Wieso dumm?
Die Timer werden alle nur genau 1x initialisiert und behalten über die gesamte Lebenszeit des Programmes ihren Speicherplatz. Soweit ich das beurteilen kann ist das Fragmentierung und ähnliches betreffend "aufkommensneutral" zu anderen Methoden, aber höchst flexibel, da ganz einfach handzuhaben und meines Wissens nach auch ziemlich nahe an "Objektorientiertheit".

Ich möcht meine eigentliche Frage, warum am PC beide Varianten klaglos funktionieren, am Arduino aber nur die umständliche Version über die unschöne Hilfsmethode, daher gerne nochmals in den Raum stellen. (Deinen Hinweis auf Templates habe ich sehrwohl registriert, aber da muss ich ert nachlesen. Die habe ich anscheinend inzwischen geistig extern ausgelagert und müssen erst wieder reaktivert werden).

Ach ja, vielleicht noch zur Info:
Compilieren läßt sich Variante am Arduino, syntaktisch stimmt also alles. Aber wenn es läuft kommt beim new nur erwähntes 0 als Pointer zurück.
Im Konstruktor einen anderen Konstruktor der selben Klasse aufzufrufen ist doch erlaubt, oder? Ich konte nichts Gegenteiliges finden.

Grüße
Wolfgang

C++11 zur Rettung!

Aktiviere erst mal C++11. Dazu gehst du nach x:\Arduino\hardware\arduino\avr\platform.txt
Dann hängst du hinten bei compiler.cpp.flags diesen Schalter an:
-std=gnu++11

Nachtrag:
Ich hatte übersehen, dass das auf dem DUE/ARM anders ist. Das oben ist nur für die AVR toolchain

In der ARM toolchain ist zumindest bei Arduino 1.6.x wohl idiotischerweise im User Verzeichnis. Also ungefähr so:
C:\Users\xxx\AppData\Roaming\Arduino15\packages\arduino\hardware\sam\1.6.x

Geht aber ansonsten genauso :slight_smile:

Jetzt kannst du einfach Konstruktoren in anderen Konstruktoren aufrufen. Aber nicht im Körper selbst, sondern wie bei Initalisierungslisten:
http://www.learncpp.com/cpp-tutorial/b-5-delegating-constructors/

Nennt sich "delegating constructors"

Serenifly:
Jetzt kannst du einfach Konstruktoren in anderen Konstruktoren aufrufen. Aber nicht im Körper selbst, sondern wie bei Initalisierungslisten:
http://www.learncpp.com/cpp-tutorial/b-5-delegating-constructors/

Nennt sich "delegating constructors"

Yesssss, das ist der entscheidende Hinweis gewesen!
Mit dem delegating constructor sieht mein leerer Konstruktor nun so aus und tut:

Meine_Timer::Meine_Timer(): Meine_Timer(1) {}

Die restlichen Teile bleiben alle gleich wie in meinem ersten, "schöneren" und vor Allem kürzeren Test.cpp.

Serenifly:
C++11 zur Rettung!

Das ist gar nicht notwendig gewesen, der DUE (wie es beim UNO aussieht muss ich noch verifizieren) legt nachdem ich den delegating construktor verwende nun brav die Speicher an, liefert "gute" Pointer bei new und tut ganz toll.
Geistig notiert ist das mit C++11 aber jedenfalls, weil das auf dem Dev++ am PC die Warning, die der bei delegating constructor wirft, verschwinden läßt. Von daher also auch ein guter Hinweis, für den ich mich bedanke.

Meine Idee war also so falsch nicht, nur ist mein erster Gedanke, wie man das umsetzt zu einfach gestrickt gewesen. An die Delegation habe ich einfach nicht mehr gedacht...

Herzlichen Dank für die kompetente und punktgenaue Hilfe!!

Grüße
Wolfgang

Also ohne das explizit per Compile-Schalter zu aktivieren schmeißt der DUE da auch eine Warnung raus! Hast du die aktiviert? Wahrscheinlich nicht.

Sowas wie "... is a C+11 feature. Only available with -std .... [activated by default]".

Also er erkennt dass es C++11 ist. Und schreibt irgendwas dazu dass es aktiviert ist, aber verlassen würde ich mich darauf nicht. Das hatte ich letztens auch mit Konstruktor-Vererbung. Da kommt das gleiche.
Andere C++11 Features wie das nullptr keyword bringen aber einen Fehler, weil er das ohne Compile-Schalter nicht kennt.

Du kannst auch so Konstruktoren in anderen Konstruktoren aufrufen. Aber das bringt leider keinen Fehler. Man sollte es wohl undefiniertes Verhalten nennen. Deshalb kompiliert es, aber es geht nicht.

Serenifly:
Also ohne das explizit per Compile-Schalter zu aktivieren schmeißt der DUE da auch eine Warnung raus! Hast du die aktiviert? Wahrscheinlich nicht.

Erwischt! Ich verwende die FastLED-Library (3.1, alle die aktuelle 3.0.3 tut nicht am DUE), und die wirft seitenweise Warnings, wenn man die Stufe "all" wählt. Daher bin ich wieder zurück-gegangen mit der Stufe, versehentlich gleich auf "none". Ist korrigiert auf "default", und nun kommt auch der Hinweis auf gnu++11.

Serenifly:
Du kannst auch so Konstruktoren in anderen Konstruktoren aufrufen. Aber das bringt leider keinen Fehler. Man sollte es wohl undefiniertes Verhalten nennen. Deshalb kompiliert es, aber es geht nicht.

Das Positive daran ist: Man hat dann etwas zum Nachdenken :slight_smile:

Grüße
Wolfgang

Hefter:
Das Positive daran ist: Man hat dann etwas zum Nachdenken :slight_smile:

Ich sehe da nichts positives dran. Es ist schwer verständlich wieso das nicht als Fehler oder wenigstens als Warnung angemeckert wird. Es ist auch gemein weil das in anderen Sprachen so geht, z.B. Java (aber mit this statt dem Konstruktor-Namen). Es geht ja sogar laut dir mit einem anderen C++ Compiler, auch wenn es laut Standard falsch ist.

C++ hat aber sehr viele Feinheiten was die Konstruktion von Objekten angeht. Deshalb kann man einiges nicht mehr machen wenn man schon im Konstruktor-Körper ist was mit Initialisierungslisten dagegen geht (z.B. Zuweisungen auf Referenz-Variablen).

"Delegating constructor" ist aber definitiv ein C++11 feature. Mit einem Compiler der gar kein C++11 kennt geht das gar nicht. avr gcc versteht wohl auch ohne dass man es aktiviert Teile des Standards und immerhin dass man C++11 verwendet, auch wenn er es nicht kompilieren kann.