C++ extern oder durchreichen oder singleton

Gast #5817129
Lesenswert?

Hallo,

Auf eine Klasseninstanz / Objekt soll in verschiedenen Klassen in 
verschiedenen Files zugegriffen werden können.

Ich sehe jetzt nur die Möglichkeiten.....
Verwenden von extern oder durchreichen von Pointern auf das Objekt oder 
die Klasse als Sinleton anzulegen.

Am saubersten aber aufwendigsten wäre wohl das Durchreichen von nem 
Pointer auf das Objekt, auf das ich in der jeweiligen Klasse zugreifen 
will.

Singleton ginge wäre aber wohl zweckentfremdet.

Extern ginge auch.... aber ... unsauberer?

Gibt es Alternativen oder was wäre die gute Lösung?
#5817136
Lesenswert?

Wie immer ist die Antwort auf die Frage "welcher Pattern passt" halt 
"kommt drauf an".

Singleton ist ok, wenn du dir absolut, 100% sicher bist, dass es nie 
Sinn macht dass es mehrere dieser Objekte gibt. Plausibler Kandidat 
wären z.B. die globalen Konfigurationsoptionen des Programms. In allen 
anderen Fällen macht durchreichen erfahrungsgemäß mittelfristig weniger 
Ärger.
Gast #5817599
Lesenswert?

Janosch schrieb:
> Am saubersten aber aufwendigsten wäre wohl das Durchreichen von nem
> Pointer auf das Objekt, auf das ich in der jeweiligen Klasse zugreifen
> will.

Genau.

> Singleton ginge wäre aber wohl zweckentfremdet.

Singleton ist das Pattern um ein Objekt einmalig zu initialisieren und 
wird praktisch nur im Kontext globaler Variablen benutzt.

> Extern ginge auch.... aber ... unsauberer?

Das wäre lediglich die zugehörige globale Variable (deren 
Initialisierung iwie sichergestellt werden muss).

Ob Singleton/Global oder Durchreichen hängt vom Kontext ab. Häufig ist 
beides gleichermaßen schlecht. ;-)
Gast #5818200
Lesenswert?

Dann formulier mal die Lehrbuchlösung.

Eine weitere mir eingefallene Lösung wäre entsprechende Membervariable 
der Klasse mit static zu declarieren.

Jede Instanz dieser Klasse kann dann auf die statische Membervariable 
zugreifen.
#5818228
Lesenswert?

Ich würde keinen Singleton verwenden. Das Pattern
hat zu viele Nachteile und ist auch aus der Mode
gekommen. Auch keine sonstigen technischen Spielereien.

Die sauberste Lösung meiner Meinung nach wäre das
injecten des "globalen" Objektes in die Objekte
hinein, die das verwenden wollen. Und das nur unter
Verwendung eines Interfaces. So werden Abhängigkeiten
minimiert.

Allerdings solltest du deine Klassenstruktur überdenken.
Wenn es viele Klassen gibt, die auf das Objekt zugreifen
wollen/müssen, stimmt was nicht.

merciless
Gast #5818298
Lesenswert?

Die Klasse soll einen 1ms Timer counter bereitstellen.
Jedes andere Klassenobjekt, das nur alle x Zeiteinheiten was machen 
soll, soll auf diese Timer Klasse zugreifen können.

Die Timer Klasse wird mittels Observer Pattern jede 1ms im Isr kontext 
upgedatet und zählt in einer TimerCount Klasse eine statische 
Membervariable hoch.

Jedes andere Objekt, das eine Zeitinformation braucht, instanziiert die 
TimerCount Klasse und hat Zeitinformationen.

Da war meine Überlegung eben, wie diese Zeitinformation anderen Objekten 
zugänglich machen.
#5818322
Lesenswert?

Janosch schrieb:
> Jedes andere Objekt, das eine Zeitinformation braucht, instanziiert die
> TimerCount Klasse und hat Zeitinformationen.

1
struct Timer {
2
// start, stop, usw. usf.
3
};
4

5
struct UseTimer{
6
// tim.start, tim.stop, usw. usf.
7
private:
8
  Timer tim;
9
};


Genau dafür solls doch Timer Objekte geben, die man in anderen Klassen 
nutzen kann? Da braucht man weder was durchreichen noch an Singleton 
usw.
#5818355
Lesenswert?

Fragen die einzelnen Klassen die "Uhrzeit" ab
und entscheiden, ob sie was machen sollen weil
z.B. 100ms vergangen sind? Wenn dem so ist, dann
würde ich da eher eine Scheduler-Klasse schreiben,
die diese Aufgabe übernimmt.

Und ein Objekt instantiieren, um an eine statische
Info zu kommen klingt mir overdosed: dafür gibt
es public static Methoden.

merciless
#5818802
Lesenswert?

Janosch schrieb:
> Dann formulier mal die Lehrbuchlösung.

Du hattest das Für und Wider bereits geschrieben. :-) Wenn du ein 
bisschen im Netz schaust, wirst du zum Thema einiges an Diskussion 
finden.

Das Durchreichen ist die "bessere" Lösung. Man kann an den 
Funktionsköpfen bereits sehen, welche Parameter verwendet werden. Die 
Kunst besteht darin, lange Parameterlisten zu vermeiden. Ansonsten wird 
der Code schnell unverständlich.

Hast du bereits ein Projekt, wo das Durchreichen quasi ein 
Refactoring/Redesign erfordert, dann greifst du eher zum globalen 
Zugriff. Das macht den Code schwerer durchschaubar, da es "versteckte" 
Parameter gibt. Dokumentation ist angesagt. (Ich habe hier so ein 
historisch gewachsenes Projekt, wo es so viele Singletons und globale 
Kontexte gibt, dass man absolut im Wald steht.)

Ist es dein eigenes Projekt, mach' es wie es für dich am "einfachsten" 
ist. Die Lehrbuchlösung gibt es leider nicht.
Gast #5819400
Lesenswert?

Oliver S. schrieb:
> Mikro 7. schrieb:
>> Das Durchreichen ist die "bessere" Lösung.
>
> Bei der Aufgabenstellung scheint es sich um ein Art Hardwarezugriff zu
> handeln (für den Timer gilt „es darf nur einen geben“). Da ist eine dann
> globale und/oder statische Instanz doch eine sinnvolle Lösung.
>
> Oliver

Für die Abstraktion des Hardwaretimers schon, aber nicht für den 1ms 
Observerpattern Timer, der ist nicht einzigartig. Was passiert z.B. wenn 
man einige der Funktionalitäten, die aktuell den 1ms Timer nutzen, auf 
0.24ms umstellen muss?
#5819694
Lesenswert?

mh schrieb:
> Für die Abstraktion des Hardwaretimers schon, aber nicht für den 1ms
> Observerpattern Timer, der ist nicht einzigartig. Was passiert z.B. wenn
> man einige der Funktionalitäten, die aktuell den 1ms Timer nutzen, auf
> 0.24ms umstellen muss?

Das seh ich auch so. Meine Vorschlag wäre daher sowas:
1
// Managed echte Hardware, schreibt Register, erhält Interrupt, usw.
2
template<typename Registers>
3
struct HwTimer{
4
  static void init() {}
5
  static void run() {}
6
  static void stop() {} 
7
};
8

9
// Logik fürs anmelden hier rein
10
struct Timer{
11
  Timer(std::function<void()> callback) {}
12
  void run(size_t ms) {}
13
private:
14
  static inline HwTimer hw_tim;
15
  std::function<void()> callback;
16
};
17

18
// Timer Instanzen nach Lust und Laune nutzen
19
struct UseTimer{
20
  ...
21
private:
22
  Timer tim;
23
};
Gast #5819697
Lesenswert?

Dirk K. schrieb:
> Allerdings solltest du deine Klassenstruktur überdenken.
> Wenn es viele Klassen gibt, die auf das Objekt zugreifen
> wollen/müssen, stimmt was nicht.

Huh? Bestes Beispiel: ein Logger. Wird überall im Programm benötigt, es 
soll aber natürlich nur eine Instanz geben.
Ist doch das Normalste der Welt :-)


Ein guter Ansatz ist, shared Pointer auf die Instanz herumzureichen. Nur 
so kann jede Klasse mit UnitTests versehen werden. Falls das zu viel 
Aufwand ist: dafür gibt es Dependancy Injection Frameworks. Die nehmen 
einem die Arbeit ab ;-)
#5819757
Lesenswert?

Quixy schrieb:
> Dirk K. schrieb:
>> Allerdings solltest du deine Klassenstruktur überdenken.
>> Wenn es viele Klassen gibt, die auf das Objekt zugreifen
>> wollen/müssen, stimmt was nicht.
>
> Huh? Bestes Beispiel: ein Logger. Wird überall im Programm benötigt, es
> soll aber natürlich nur eine Instanz geben.
> Ist doch das Normalste der Welt :-)
>
>
> Ein guter Ansatz ist, shared Pointer auf die Instanz herumzureichen. Nur
> so kann jede Klasse mit UnitTests versehen werden. Falls das zu viel
> Aufwand ist: dafür gibt es Dependancy Injection Frameworks. Die nehmen
> einem die Arbeit ab ;-)

Ja, das ist dann genau die Ausnahme, die die Regel bestätigt ;)
Ich hatte eher einen Timer im Sinn, den Zugriff auf Hardware
oder ähnliches.

Und man braucht nicht gleich ein DI-Framework. In meinen
Applikationen mache ich das von Hand. Die Objekte werden
in einer Klasse erzeugt und Abhängigkeiten injiziert.
So kann ich das an zentraler Stelle ändern (für Tests etc).

merciless
#5820862
Lesenswert?

Sven B. schrieb:
> Dirk K. schrieb:
>> In einer funktionalen Welt ja, in einer
>> objektorientierten hat man eine Instanz.
>
> Es gibt ja rein theoretisch auch die Möglichkeit, ein bisschen
> pragmatisch nachzudenken und nicht blind einem Pattern nachzulaufen ...

Jo, ich bin auch Pragmatiker. Der TO hatte
aber nach einer guten Lösung gefragt, nicht
nach einer pragmatischen. Overengineering
ist eh eines der größten Probleme beim Entwerfen
von Software-Architekturen.

merciless
Gast #5821703
Lesenswert?

Sven B. schrieb:
> Es gibt ja rein theoretisch auch die Möglichkeit, ein bisschen
> pragmatisch nachzudenken und nicht blind einem Pattern nachzulaufen ...

So ein Logger braucht nun mal eine ganze Menge an Daten:
 - File Handles (wenn er in dateien Loggt)
 - Netzwerkverbindungen (wenn er auf einem Server loggt)
 - Thread-Handles (da das Logging vorzugsweise in einem abgekoppelten 
Thread laufen soll)
 - Queues für die Logbotschaften
 - Timer für das Zeitstempeln der Botschaften
 - Etc.

Das lässt sich wunderbar in einer Klasse kapseln. Warum sollte man sowas 
nicht OOP lösen wollen?
Gast #5821834
Lesenswert?

Quixy schrieb:
> So ein Logger braucht nun mal eine ganze Menge an Daten:
>  - File Handles (wenn er in dateien Loggt)
>  - Netzwerkverbindungen (wenn er auf einem Server loggt)
>  - Thread-Handles (da das Logging vorzugsweise in einem abgekoppelten
> Thread laufen soll)
>  - Queues für die Logbotschaften
>  - Timer für das Zeitstempeln der Botschaften
>  - Etc.

Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen, 
eine rfc5424 (wenn man structured data braucht) oder rfc3164 konforme 
Logmeldung zusammensetzen, und dann mit send die Logmeldung 
wegschreiben. Fertig. Filtern, übers Netzwerk, in Dateien schreiben, 
etc. erledigt der Systemlogger. Für rfc3164 gibts sogar eine Standard 
Funktion dafür: syslog(3). Ernsthaft, es gibt nichts nervigeres als sich 
mit den Logs solcher abnormalen Programme Herumschlagen zu müssen und 
aufzuräumen, die meinen, sie könnten das besser selbst und anders lösen. 
Selbst nach stdout loggen und durch logger pipen ist besser als was man 
sonst so für mist in den "professionellen" Programmen sieht.
Gast #5821866
Lesenswert?

DPA schrieb:
> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen,

Das mag für deine 200-Zeilen-Pickel-Nerd-Linux-Progrämmchen gelten, aber 
es gibt Bereiche im echten Leben, die haben höhere Anforderungen z. B. 
Medizingeräte und deren Anwendungssoftware.
Gast #5821870
Lesenswert?

Quixy schrieb:
> DPA schrieb:
>> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen
>
> Sicher, dass das auch auf Windows, MacOS und Android funkioniert?

Auf MAX OS X ist es glaub ich /var/run/syslog, auf BSD und anderen 
unixoiden wurde es teils nach /var/run/log verschoben. Muss man halt mit 
stat die 3 Dateien testen, bis man eine gefunden hat. Und bei Windows 
und Android macht man halt entweder ein ifdef und reicht die Daten der 
Logfunktion an die Systemnative weiter, oder linkt einfach eine 
alternative Implementation der Funktion für die Platform, genauso wie 
man das bei allen anderen Systemspezifischen Sachen auch macht. 
Vermutlich lassen sich auch dafür irgendwo schon fertige logging 
libraries finden, wenn man das Rad nicht unbedingt neu erfinden will.
Gast #5821898
Lesenswert?

Professioneller schrieb:
> DPA schrieb:
>> Nein, ein Logger muss nur 3 dinge können: /dev/log unix socket öffnen,
>
> Das mag für deine 200-Zeilen-Pickel-Nerd-Linux-Progrämmchen gelten, aber
> es gibt Bereiche im echten Leben, die haben höhere Anforderungen z. B.
> Medizingeräte und deren Anwendungssoftware.

Wie gesagt, syslog ist ein standard, genau wie die syslog Funktion. 
Gerade in diesen Umgebungen sollte man sie nutzen, statt selbst was über 
kompliziertes zu stricken. Kliniken haben in der regel auch eine 
IT-Infrastruktur mit Syslog Server, zumindest wird das in manchen PDFs 
erwähnt, wenn man "Medizin Syslog" googelt. Wobei, spezialisierte 
Medizingeräte haben vermutlich ein komplett anderes System als anderes 
zeug, und wenn dort überhaupt was geloggt wird, dann in der regel keine 
Daten um das Programm zu analysieren, sondern Patientendaten, wann es 
sich einschalten und auf irgendwelches zeug reagieren musste, und 
solches zeug, also keine Logmeldungen in dem sinne, und damit auch nicht 
mit normalem logging in anderen bereichen vergleichbar. In diesen 
Geräten wird man im Hauptprogramm wohl auch keine spezielle Logger 
Klasse finden, vermutlich gar keine Klassen. Ironischerweise sind die 
Systeme oft trotzdem, oder gerade deshalb, oft weniger sicher als 
sonstige, normale Systeme. Dafür laufen keine dafür aber wohl keine 
Logfiles voll.
Gast #5821908
Lesenswert?

DPA schrieb:
> und wenn dort überhaupt was geloggt wird, dann in der regel keine
> Daten um das Programm zu analysieren

Natürlich, unter anderem genau dafür loggt man, um nachzuvollziehen was 
im Feld passiert ist.
Ich habe das Beispiel Medizin gebracht, weil ich selbst seit >20 Jahren 
Software für Computertomographen schreibe. Früher C++/MFC, heute C#/.NET 
(unter Windows 10 :P)
Gast #5821934
Lesenswert?

Professioneller schrieb:
> DPA schrieb:
>> und wenn dort überhaupt was geloggt wird, dann in der regel keine
>> Daten um das Programm zu analysieren
>
> Natürlich, unter anderem genau dafür loggt man, um nachzuvollziehen was
> im Feld passiert ist.
> Ich habe das Beispiel Medizin gebracht, weil ich selbst seit >20 Jahren
> Software für Computertomographen schreibe.

Nicht aus dem Kontext reissen:

DPA schrieb:
> Wobei, spezialisierte
> Medizingeräte haben vermutlich ein komplett anderes System als anderes
> zeug, und wenn dort überhaupt was geloggt wird, dann in der regel keine
> Daten um das Programm zu analysieren

Mit "spezialisierte Medizingeräte" meine ich Herzschrittmacher, 
Blutreinigungsgeräte, und solche speziellen Geräte. Software für normale 
PCs bei weniger direkt lebensgefährlichen Systemen würde ich eher wie 
die restlichen PCs in dem Umfeld betrachten, ob das jetzt in einem 
Computertomographen steckt oder nicht. Das sind komplett andere arten 
von Systemen.

Professioneller schrieb:
>> OMG, Ich wünschte ich müsste nicht alle Krankenhäuser meiden, wenn ich
>> dem entkommen wollte.
>
> Ich sehe ein Weltbild einstürzen, ...

Keine angst, das da etwas zu viele Windowssysteme in Krankenhäusern 
herumstehen war mir schon klar. man liest ja auch immer mal wieder, wenn 
die IT dort mal wieder vergisst die Updates oder ein mit der 
Spezialsoftware inkompatibles Antivirusprogramm auszuschalten, und ne OP 
dann deshalb abgebrochen werden muss, usw. Und dann gabs ja auch mal 
noch die durch WannaCry lahmgelegten Krankenhäuser...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren