Read-only für Klassen-Variablen?

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Read-only für Klassen-Variablen?

Beitrag von starcow »

Tach Leute

Ich suche nach einer Möglichkeit, wie ich Member-Variablen von Klassen gegen aussen hin schreibschützen lassen kann.
Der lesende, direkte Zugriff soll aber weiterhin möglich sein.
Ich möchte mir damit vor allem das ständige schreiben von get-Funktionen ersparen. Zudem finde ich es grundsätzlich bisschen besser lesbar, wenn man direkt auf die Variable zugreifen kann.

Ist sowas irgendwie machbar? Ich hatte an const gedacht, doch der Wert der Variable soll ja durch eine eigene Klassenmethode weiterhin zur Laufzeit veränderbar bleiben.
Also quasi folgendes:

Code: Alles auswählen

// möglich
int x;
x = Player->kraft;

// nicht möglich
int x = 10;
Player->kraft = x;
Ich stosse immer wieder auf solche Situationen, in der ein read-only modus nützlich wäre. Ich fürchte aber, es geht nicht. :)

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von Schrompf »

Ich rücke davon zwar gerade wieder ab, aber ich verstehe Deinen Wunsch. Du suchst Getter. Sowas:

Code: Alles auswählen

class Player {
// ist von haus aus private
int kraft;

public:
  int GetKraft() const { return kraft; }
};
Lesender Zugriff ist jetzt player.GetKraft(), schreiben gibt Gemecker vom Compiler.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: Read-only für Klassen-Variablen?

Beitrag von NytroX »

Ich würde private/protected o.ä. eigentlich garnicht mehr nutzen.
Normalerweise besteht ein Programm aus mehreren Modulen, die über (mehr oder weniger) fest definierte Interfaces miteinander reden.
Und da würde ich dann auf Zugriffsrechte achten; aber innerhalb eines Moduls ist das meistens eher overhead.
Zumindest machen das einige neuere Programmiersprachen auch so.

Aber wenn du es tatsächlich brauchst, ist das in C++ etwas schwieriger als in anderen Sprachen.
Es hat zwar einige Einschränkungen, aber vielleicht geht es für dich so in der Art:

Code: Alles auswählen

template<typename T, typename U>
struct ReadProperty {
	friend U;
private:
	T value;
	const T& operator=(const T& other) {
		value = other;
		return other;
	}
};

struct Test {
	ReadProperty<int, Test> p; //etwas unschön, dass man den 2. Parameter braucht
	void testfn() {
		p = 2; //geht
	}
};

int main() {
	auto t = Test{};

	auto y = t.p; //geht
	t.testfn(); //geht
	t.p = 3; //geht nicht (compiler error)

	return 0;
}
Vermutlich müsste man das aber noch etwas erweitern, um es wrklich brauchbar zu machen.
Zum Vergleich mal in C#:

Code: Alles auswählen

class Test{
  public Int p { get; private set; }
}
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von starcow »

@Schrompf
Danke Schrompf! :)
Also dann führt wohl kein Weg an einer Get-Methode vorbei.
Was hat es denn eigentlich mit dem Schlüsselwort const bei der Methoden-Definition auf sich?
Ich meine, man gibt ja sowieso nicht die tatsächliche Variable "kraft" per return zurück, sondern lediglich eine Kopie deren Werte. Daher dürfte die Variable kraft ja ohnehin nicht veränderbar sein. Oder ist das ganze eher kosmetischer Natur?
Und darf ich fragen, wieso Du von diesem Konzept wieder abrückst?

@NytroX
Vielen Dank für die ausführliche Erklärung.
Versteh ich Dich richtig: Du würdest einfach alles in einer Klasse auf public setzen?
Ich bin mit Modulen eigentlich noch nicht so wirklich in Berührung gekommen. Kann man das als neue (Denk)-Richtung in der Programmierung verstehen?
Folglich dürfte es wohl sinnvoll sein, dass ich mich damit auseinandersetze.
Hast Du vielleicht eine Empfehlung für mich, was das Thema angeht?

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von Schrompf »

starcow hat geschrieben:@Schrompf
Danke Schrompf! :)
Also dann führt wohl kein Weg an einer Get-Methode vorbei.
Was hat es denn eigentlich mit dem Schlüsselwort const bei der Methoden-Definition auf sich?
Ich meine, man gibt ja sowieso nicht die tatsächliche Variable "kraft" per return zurück, sondern lediglich eine Kopie deren Werte. Daher dürfte die Variable kraft ja ohnehin nicht veränderbar sein. Oder ist das ganze eher kosmetischer Natur?
Und darf ich fragen, wieso Du von diesem Konzept wieder abrückst?
a) Das const hinter der Parameterliste einer Methode bedeutet: diese Methode darf das Objekt nicht verändern. Also: Du darfst die Funktion auch benutzen, wenn das Objekt Dir als const übergeben wurde. Stell Dir bei eine Funktion vor, die ausrechnen soll, ob der Spieler eine Tür eingetreten bekommt. Dazu braucht die Funktion den Spieler, die Tür, und evtl. noch Umgebungsbedingungen. So eine Funktion könnte so aussehen

Code: Alles auswählen

bool CanPlayerCrackDoor( const Player& player, const Door& door ) { ... }
Die Entscheidung, ob der Spieler die Tür aufkriegt oder nicht, wird erstmal durch reines Draufgucken entschieden - Spieler und Tür bleiben dabei unverändert. Also kriegt die Funktion Tür und Spieler nur als const reference-Parameter. const, weil mit dieser Funktion was faul wäre, wenn sie bei der Entscheidungsfindung den Spieler oder die Tür verändern müsste, und reference, weil sonst ne Kopie angelegt wird, und das wird unter Umständen ein bisschen mehr Rechenzeit fressen als nötig.

Wenn Du jetzt CanPlayerCrackDoor() implementieren willst, möchtest Du dabei höchstwahrscheinlich die Kraft des Spielers einberechnen. Du hast aber nur ein const-Objekt, also gehen nur Funktionen, die const sind.

Weit ausgeholt, hm? Aber ich hoffe, es ist nachvollziehbar.

und b) warum ich davon abrücke. Auf Arbeit kapsele ich viel. Das ist bei vielen Programmierern und dem Arbeitsumfeld auch eine gute Sache, denn es kann zu besserem Code führen, wenn jeder dazu genötigt wird, saubere Schnittstellen zu definieren und nur diese zu benutzen. Und zu Hause habe ich eigentlich auch immer nach "gutem Code" gestrebt. Bis ich das xte Mal an irgendner Stelle ein neues Interface eingefügt habe, nur um auf irgendwas sauber Zugriff zu kriegen, und es war ein bissl schmerzhaft, und irgendwann... hey moment... wozu tue ich mir das eigentlich an? Ich bin doch der einzige Coder, der diesen Code jemals zu Gesicht bekommt. Unittests schreibe ich eh nur für fundamentale Funktionen und ein paar Klassen, die ich wirklich überall verwende. Wie meine Custom Containers und Allokatoren und so. Warum zur Hecke also mache ich mir solche Mühen?

Dazu kommt, dass ich nach locker 20 Jahren der Freude an Basisklassen und Ableitungshierarchien, um Spielobjekte in Monster, Deko, Spieler, Schüsse, usw zu spezialisieren, jüngst das Entity Component Pattern für mich entdeckt habe. Und in einer noch nicht allzu spezialisierten Welt ohne viele Sonderfälle ist diese Art des Programmierens auch sehr direkt und angenehm. Ich schreibe jetzt vor allem sehr viel weniger Code, und zumindest für wirklich große Stückzahlen von Objekten ist diese Art von Code auch besser für die modernen Prozessoren.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: Read-only für Klassen-Variablen?

Beitrag von NytroX »

Ja, ich würde alles public lassen, und in namespaces kapseln.

Und sorry, ich sollte in der Wortwahl wohl ewas genauer sein.
Mit "Modul" meinte ich jetzt allgemein ein Konstrukt, welches mehrere Klassen/Funktionen enthält; also eine Executable enthält mehrere Module, ein Modul enthält mehrere Klassen/Funktionen/Variablen.
Ich denke "namespace" trifft es momentan am ehesten in C++. In Java oder in Go heißen sie Packages, in D heißen sie Module; C++ kriegt vielleicht auch bald "echte" Module. (Da gibt es kleinere Unterschiede, aber im Grunde ist es ähnlich).

Was ich mit "neuere" Sprachen meinte:
In Go gibt es gar kein public/protected/private, sondern eine sehr einfache Regel, die die meisten ganz cool finden (weil einfach und effektiv):
if the first letter is capital, it is visible outside the package. That’s it.
In Java gibt es einen ähnlichen default:
No access modifier at all means that the method or variable defaults to package protected.
Oder in Rust:
Visibility works at the module level. If you want module X to have access to an item in module Y, module Y must make it public.
Ich glaube die Tendenz wird klar... und warum sollte ich mich in C++ mit einem Konzept rumschlagen, das offenbar selbst für die Strukturierung großer Software nicht notwendig ist? :mrgreen:
Aber mal im Ernst: das Problem besteht hauptsächlich in der Wartung von Code: Wenn man jetzt doch merkt, dass man von der Klasse erben will oder so, muss man vieles von "private" auf "protected" ändern (aber ja nicht alles). Wenn man das korrekt implementieren will bei jeder Klasse, bei jeder Änderung... das ist einfach zu krasses Micromanagement und wohl eher eine Arbeitsbeschaffungsmaßnahme. Wem das Spaß macht, der darf das natürlich gerne machen, aber ich ändere einfach jedes "class" in "struct" und lasse alle Access-Modifier komplett weg.

z.B. Wenn man den Code des Spiels von der Engine trennen will (ist jetzt weit hergeholt, weil der Engine-Code vermutlich sowieso in einer eigenen DLL ist, aber egal):

Code: Alles auswählen


//engine.h
#pragma once
#include <memory>

namespace engine {
	struct RenderObject {};
	void render(const RenderObject& ro);
	std::shared_ptr<RenderObject> createCircle(int radiusInPixels);
}

//engine.cpp
#include "engine.h"


namespace //anonymer namespace, verhindert zugriff
{
	struct Circle : engine::RenderObject
	{
		int radius;
	};
}

namespace engine
{
	void render(const RenderObject& ro) {
		//...
	}

	std::shared_ptr<RenderObject> createCircle(int radiusInPixels)
	{
		auto p = std::make_shared<Circle>();
		p->radius = radiusInPixels;
		return p;
	}
}

//game.cpp
#include "engine.h"

namespace game
{
	inline void start()
	{
		auto myCircle = engine::createCircle(10);
		//auto c = new engine::???::Circle(); //keine chance
		engine::render(*myCircle);
	}
}

int main() {
	game::start();
	return 0;
}
Das ist jetzt aber nur, was ich persönlich mache, weil ich gemerkt habe, dass ich Änderungen und Refactorings so am schnellsten und unkompliziertesten machen kann.
Andere machen das eben anders, und daran ist auch nichts falsch :-)
Wenn man in einem Team arbeitet, ist die Konsistenz der Code-Convetions oder Code Stile viel wichtiger als den "besten Stil" zu finden; aber wenn es um eigene Projekte geht, haben wir ja alle Freiheiten der Welt :D


Const ist so eine Sache.
Dem Compiler hilft es bei der Optimierung des Codes erstmal nicht, der kriegt das schon alleine raus, ob das Objekt modifiziert wird oder nicht.
Ist also eher was zum "Selbstschutz", ist aber garnicht schlecht.
Das Ding ist: entweder man macht es überall richtig, oder garnicht, weil der Compiler das checkt und einem die Hölle heiß macht, wenn man das "halbherzig" angeht :lol: .
(Schlimm wirds nur dann, wenn man Libraries von anderen benutzt, die das nicht richtig implementieren...aber dafür hat man ja const_cast...)
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: Read-only für Klassen-Variablen?

Beitrag von odenter »

"Einfach" alles public machen hört sich für mich grauenvoll an. Das kann man vielleicht machen wenn man der einzige ist der eine solche Klassen verwendet, weil wir von einem Hobby Projekt reden.
Reden wir aber von Klassen die von vielen Entwicklern verwendet werden und von denen ggf. auch andere Menschen erben und eigene Klassen erzeugen ist "einfach alles public" so gar keine Option, z.B. wenn es um ein API Design für andere Menschen geht.

Das gefährliche an "public" Variablen sind Menschen die diese dann auch von außen setzen und irgendwann kommt eine Anforderung hier was zu ändern und dann überarbeitest Du im schlechtesten Fall ~8500 Programmstellen (habe ich schon erlebt... ~25 Jahre alte Software...).

Interfaces sind toll, aber das was in c# mit "private set" geht müsste man doch mit Templates nachbauen können, bin nicht so der Template Crack. Aber es muss doch möglich sein ein Template zu bauen dem ich ein BOOL für Set und einen für das Get mitgebe und der das dann prüft und im Idealfall einen Compilerfehler, oder Laufzeitfehler, bei falscher Benutzung wirft. In irgendeinem "Game Programming Gems 8 (?)" Buch meine ich war mal sowas, REgistered Variables oder so ähnlich haben die das geschimpft, meine ich (kann mich auch irren).
Weil ich neugierig war habe ich gerade mal ein bischen gesucht, es gibt gute Gründe für und gegen Templates als setter/getter (finden sich bei Stackoverflow).
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: Read-only für Klassen-Variablen?

Beitrag von NytroX »

Oha, ich glaube da hast du mich falsch verstanden, ich wollte damit nicht sagen, dass da ein Code-Gewurschtel entstehen soll und alles von überall aus erreichbar ist :mrgreen:
Das gefährliche an "public" Variablen sind Menschen die diese dann auch von außen setzen
Stimmt. Aber heißt das im Umkehrschluss wirklich, dass alles, was public ist, von überall aus im gesamten Programm nutzbar sein soll?
Nur weil ich in meiner Library was public mache, weil eine andere Klasse aus meiner Library drauf zugreift, heißt das ja nicht, dass der Nutzer meiner Library das automatisch auch benutzen können soll. Das gilt auch für Methoden und/oder Funktionen.

Ich denke halt, dass "public" und "private" auf der falschen Ebene arbeiten (Klassen statt Module), und deshalb das von dir beschriebene Problem eben genau nicht lösen können.
(Als Beispiel kann ich da noch Java9 erwähnen, die haben das auch schmerzlich erfahren und wollen neuerdings den Zugriff auch auf Modulebene regeln: http://openjdk.java.net/jeps/260. Hat halt nur 20 Jahre gedauert, bis sie drauf gekommen sind, das wollte ich euch ersparen... :lol: )

Schau dir mein Beispiel nochmal an; obwohl die Klasse "Circle" public ist, und das Feld/Member "radius" auch, sind beide von außen nicht erreichbar. So kann man das Programm schön aufteilen und erhält eine saubere Architektur.
Aber wie gesagt, jeder kann das machen, was er will, im Hobbybereich sowieso. Wollte nur einen Denkanstoß geben, wo der Trend momentan so hingeht. :)
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: Read-only für Klassen-Variablen?

Beitrag von odenter »

NytroX hat geschrieben: Schau dir mein Beispiel nochmal an; obwohl die Klasse "Circle" public ist, und das Feld/Member "radius" auch, sind beide von außen nicht erreichbar. So kann man das Programm schön aufteilen und erhält eine saubere Architektur.
Aber wie gesagt, jeder kann das machen, was er will, im Hobbybereich sowieso. Wollte nur einen Denkanstoß geben, wo der Trend momentan so hingeht. :)
Für mein empfinden widerspricht eine public Variable der Kapselung, da auf einmal Dinge die ein Objekt beschreiben und über Methoden verändert werden sollen von außen verändert werden können.
Es ist mir doch auch mit Deinem Beispiel möglich eine Instanz von Circle zu erstellen und es nicht so zu benutzen wie es gedacht war, oder nicht?
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: Read-only für Klassen-Variablen?

Beitrag von NytroX »

Nein, ist im Beispiel nicht möglich. :mrgreen:
Auf "Circle" kann außerhalb der engine.cpp nicht zugegriffen werden, d.h. man kann in der game.cpp weder einen Circle erstellen, noch auf seine Member zugreifen.
Erstellen geht nur über die Factory-Funktion, die dafür von der Engine bereitgestellt wird. Der Grund liegt im anonymen namespace, alle Funktionen und Klassen da drin sind außerhalb der Übersetzungseinheit nicht sichtbar, innerhalb aber schon (deshalb kann createCircle darauf zugreifen)..

Würde ich den radius einfach "private" machen, könnte createCircle auch nicht drauf zugreifen. Würde ich ihn nur "public" machen, könnte createCircle zwar drauf zugreifen, game.cpp aber auch! Und wir wollen ja genau verhindern, dass der User (in diesem Fall das Game) Unsinn damit macht :lol:
Um also die gleiche Kapselung hinzubekommen (Engine hat Zugriff, Game aber nicht), müsste ich ihn private machen, und aus createCircle einen "friend" machen... und wenn wir das überall so machen, fängt halt das Riesengefrickel an und der Code wird tendenziell Chaos, weil jeder "friend" von jedem wird. ;)

Aber ich würde auch gerne hören, wie ihr euren Code so organisiert; vielleicht gibts da ja bessere Möglichkeiten, bin ja selbst immer am Lernen und auf der Suche nach der besten Lösung.
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: Read-only für Klassen-Variablen?

Beitrag von odenter »

NytroX hat geschrieben:Nein, ist im Beispiel nicht möglich. :mrgreen:
Ok, ich hatte mir ein kl. Beispiel gebaut, da ging es. War dann wohl nicht wirklich vergleichbar. :)
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von Schrompf »

Nuja, meine Lösung hab ich ja oben beschrieben: professionell auf Arbeit viel interfacen und alle Member private. Zu Hause quasi nur noch structs und Ableitungen nur, wo wirklich zur Laufzeit unterschiedliches Verhalten nötig ist.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von starcow »

Danke für die interessanten Einblicke NytroX.
Ich müsste wohl noch ein besseres Verständnis von den namespaces bekommen, um deine Überlegungen nachvollziehen zu können.
Wie bist du denn auf diese Idee gekommen. Hast du evtl. einen Link zur Hand, der sich mit diesem Thema beschäftigt?

Die Idee hinter der Methode von Schrompf - erstmal zu überlegen, ob man die einzige Person sein wird, die den Code zu Gesicht bekommt - leuchtet schon ein.
Ich frage mich allerdings, ob man sich dann nicht vorwerfen lassen muss, "schlechten" Code zu schreiben? :mrgreen:

Beim Lernen von C/C++ habe ich eigentlich auch heute noch das Problem, dass einem die Grundlagen über verschiedene Quellen wirklich super vermittelt werden.
Bei "fortgeschrittenen" Themen will es mir nicht so recht gelingen, gutes Lernmaterial zu finden.
z. B. sehe ich immer noch nicht so recht durch, wie man den Code schlau in verschiedene Header-Files und .cpp Files aufteilt - ohne denn Überblick zu verlieren.
Da hatte ich Beispiele gesehen, in welchen Dateien includiert wurden, in welchen dann weitere Dateien includiert wurden - Teils über drei "Verschachtelungs-Stufen" hinaus.
Auch wenn jetzt einige Schuhe nach mir werfen werden - zur Zeit läuft mein Code noch in einem einzigen File! *duck* :mrgreen:
In meinen Büchern wurde das Thema eher stiefmütterlich behandelt. Klar, die Kompilierzeiten werden sich dadurch verbessern - was aber bei meinem Code noch länger nicht ins Gewicht fallen dürfte.
Wenn euch auch da ne Quelle bekannt sein sollte, die sich diesem Thema annimmt, wäre ich für einen Hinweis sehr dankbar.

@ Schrompf
Schrompf hat geschrieben:

Code: Alles auswählen

bool CanPlayerCrackDoor( const Player& player, const Door& door ) { ... }
Die Entscheidung, ob der Spieler die Tür aufkriegt oder nicht, wird erstmal durch reines Draufgucken entschieden - Spieler und Tür bleiben dabei unverändert. Also kriegt die Funktion Tür und Spieler nur als const reference-Parameter. const, weil mit dieser Funktion was faul wäre, wenn sie bei der Entscheidungsfindung den Spieler oder die Tür verändern müsste, und reference, weil sonst ne Kopie angelegt wird, und das wird unter Umständen ein bisschen mehr Rechenzeit fressen als nötig.

Wenn Du jetzt CanPlayerCrackDoor() implementieren willst, möchtest Du dabei höchstwahrscheinlich die Kraft des Spielers einberechnen. Du hast aber nur ein const-Objekt, also gehen nur Funktionen, die const sind.

Weit ausgeholt, hm? Aber ich hoffe, es ist nachvollziehbar.
Danke Schrompf, für dieses sehr bildhafte Beispiel! :-)
Entschuldige dass ich nochmals nachhake - aber ein Punkt versteh ich noch recht.
Dass du der Funktion CanPlayerCrackDoor nur konstante Referenzen als Parameter übergibst, leuchtet ein.
Schliesslich möchtest du ja nicht, dass dir die Funktion Werte des Player- oder Door-Objekt verändert.
Und für ein Call-by-Value dürfte das Player- / Door-Objekt zu gross sein. (Alternativ zur Referenz dürfte hier wohl ein const const Zeiger ebenso gut funktionieren - nehm ich an)
Mit dieser Konstruktion kann ich jetzt doch auf die Attribute des Players zugreifen. Für eine Kalkulation, in der die Stärke des Players verrechnet wird, brauche ich ja bloss lesend auf die Player-Attribute zuzugreifen.
Solange ich ja nicht versuche, der Member-Variable "kraft" einen neuen Wert zuzuordnen, müsste doch alles im Butter sein.

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: Read-only für Klassen-Variablen?

Beitrag von odenter »

Schrompf hat geschrieben:Nuja, meine Lösung hab ich ja oben beschrieben: professionell auf Arbeit viel interfacen und alle Member private. Zu Hause quasi nur noch structs und Ableitungen nur, wo wirklich zur Laufzeit unterschiedliches Verhalten nötig ist.
"Entity Component"(en) bringen ja noch eine Reihe weiterer Vorteile. Beziehnungen ändern sich von "Objekt ist ein" zu "Objekt hat ein". Neue Componenten lassen sich sehr einfach neuen Objekten hinzufügen. Wenn dann die Objekte statt der Referenz oder dem Pointer nur ein Handle zum Component speichern und die Komponenten zentral verwaltet werden ist laden/speichern von Zuständen einfacher.
Alles ist besser als alles "public". :)

Wer definiert denn was schlechter Code ist? Mit wachsender Erfahrung wird man immer sagen dies oder jenes war schlecht.
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: Read-only für Klassen-Variablen?

Beitrag von NytroX »

Wer definiert denn was schlechter Code ist? Mit wachsender Erfahrung wird man immer sagen dies oder jenes war schlecht.
Vollkommen richtig. Guten oder schlechten Code bemerkt man normalerweise erst nach ein paar Jahren, wenn man den Code immer wieder verändert.
Vor allem im professionellen Bereich fällt es auf.
Man hat z.B. 2 Programme, die beide 6 Jahre alt sind, und überlegt sich, in welchem man die neue Funktionalität abbilden soll, und lässt z.B. das Java und das C++ Team schätzen.
Erst dann fällts auf: Für die gleiche Funktionalität braucht das C++ Team 1 Woche, das Java Team 1 Monat.
Kleiner Tipp: der Unterschied liegt nicht an der Programmiersprache :lol: (Man kann in jeder Sprache guten und schlechten Code schreiben)
Deshalb braucht man mMn auch Senior-Developer, die schon Software über den gesamten Lifecycle (Entwicklung, Wartung, Betrieb, Migration) hinweg betreut haben, um das Programm von Anfang an gut zu strukturieren.

Im privaten Bereich bleibt nur: learning by doing :-)
Und viel Refactoring. :D
Wenn man mehrere Dateien verwendet und versucht, den Code sinnvoll aufzuteilen, dann fallen einem die Unwegsamkeiten erstmals richtig auf.

Grundsätzlich würde ich sagen, je mehr Programmiersprachen man kennt und wirklich benutzt hat, um so mehr ändert sich der eigene Programmierstil; weil man neue Pattern und Denkweisen lernt.
Z.B. kann ich C++, D, Lisp, Java, C#, Go, Rust, PHP, Javascript, Typescript, uvm. und bin vor allem auch einigermaßen mit deren Denkweisen vertraut (nur die Syntax der Sprache lernen reicht nicht). Daher im obigen Post von mir auch die Beispiele aus anderen Sprachen.
Da der Code aber so einfach wie möglich bleiben soll, damit ihn auch jeder verstehen kann (bei einem Team halt super-wichtig; bei meinem Gedächtnis aber auch :lol: ), ist mein "Stil" quasi der "kleinste gemeinsame Nenner" aus einer Vielzahl an Sprachen.

Dann kann man entscheiden, was man benutzen will und was nicht.
"ooh, ich hätte gerne die CompileTimeFunctionExecution aus D in C++" => plötzlich gibts constexpr
"ooh, ich hätte gerne das MemoryManagement aus Rust in C++" => plötzlich gibts die GuidelineSupportLibrary für VisualStudio
"ooh, ich hätte gerne die Module aus Java in C++" => plötzlich gibts einen Module_TS
"ooh, ich hätte gerne das private aus C++ in Go..." => habe ich noch nie gedacht :lol:

Zu "private" gibts in vielen (vor allem neueren) Sprachen kein analoges Sprachkonstrukt. Ich sehe (zumindest im Moment) auch keinen "unschlagbaren Vorteil" an dieser Stelle.
Aber nur die Zeit wirds zeigen, vielleicht denke ich das ja doch irgendwann.


Entity Component Systeme ist auch ein sehr gutes Beispiel. Vor 10 Jahren hätte jeder gesagt: "WTF?!?? wie kann man so coden?!??".
Mittlerweile hat sich aber gezeigt, dass wenn man weniger Abhängigkeiten im Code hat, das doch ganz gut sein kann; und viele verzichten auf den reinen objektorientierten Ansatz und tiefe Hierarchien.
(Bis das aber bei manchen Firmen in den Coding-Guidelines erlaubt wird..., ich stelle mir gerade vor, wie ich einen Teil des Codes in einem ECS abbilde... meine Kollegen bringen mich sowas von um :lol: )
Außer Compiler-Entwickler, ASTs mit Visitor ist da eher dominierend - mal sehen, wie lange es dauert, bis sie drauf kommen, dass sich da ein ECS auch gut machen könnte, weil alle Visitors plötzlich 100 Mal schneller sind... :mrgreen:
Ich persönlich habe aber festgestellt, dass die Implementierungen alle noch eher experimentell sind, vor allem wenn es um den erhofften Geschwindigkeitsvorteil geht. Aber da wird in naher Zukunft einiges passieren, nehme ich an; besonders weil Unity3d da mittlerweile mitmischt, und die brauchen den Speed.

Links:

Anonyme namespaces:
https://www.youtube.com/watch?v=HrFtpSH-Eso
https://www.gamedev.net/forums/topic/66 ... namespaces
https://stackoverflow.com/questions/154 ... -functions
https://www.reddit.com/r/cpp/comments/4 ... namespaces

Module_TS:
https://www.youtube.com/watch?v=IA14LXnBcJg (Minute 31:00 zeigt auch anonyme namespaces als "common practice")
http://www.open-std.org/JTC1/SC22/WG21/ ... /n4465.pdf

Memory Management in C++ / GSL:
https://www.youtube.com/watch?v=JtMPGwA3MzQ
https://www.youtube.com/watch?v=JfmTagWcqoE
joggel

Re: Read-only für Klassen-Variablen?

Beitrag von joggel »

Ich gebe auch mal meinen Senf zu diesem (offtopic?) Thema^^
Was du, NytroX sagts, kann ich ebenfalls zu 95% bestätigen, gerade das was professionelle SW-Entwicklung angeht. Vieles was ich so erlebt habe, kommt mir auch eher wie "Experimente" im SW-Design vor...und was es bringt zeigt erst die Zeit, nach ein paar Jahren, wie der Code wartbar und erweiterbar ist...

Zum Thema "public/private" in C++:
Ich finde es wirklich wichtig privat und public zu verwenden!!
Private bei Member und Methoden die für zugriff von außen geschützt werden sollen ist mMn absolut essenziell!

Ich experimentiere gerade bei der Action etwas mit Strukturen und Design, und ich habe irgendwie ein unsicheres Gefühl dabei, wenn ich weiß das Variablen von überall aus geändert werden können. Gerade bei Variablen die noch andere beeinflussen. Ich werde definitiv bei dem private/public-Ansatz bleiben.

P.s.: thx für die Links...

Just My 2 Cent
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von starcow »

Mich hat das Thema nicht losgelassen und ich habe mir nochmals Gedanken darüber gemacht.
Dadurch ist mir folgende Idee gekommen:
Will man eine Klassen-Variable "schreibschützen" deklariert man die eigentliche Variable als "private".
Nun deklariert man eine konstante Referenz auf diese Variable, die öffentlich zugänglich ist.
Über die Initialisierungsliste im Konstruktor wird gleich beim Erstellen der Instanz die Referenz auf die als "private" deklarierte Variable "zugewiesen".

Das Resultat davon ist, dass sich die eigentliche "Haupt-Variable" zwar intern verändern lässt, gegen aussen hin aber schreibgeschützt ist.

Neben dem einfacheren Syntax und dem Wegfallen einer "Get-Methode" müsste ein weiterer Vorteil darin bestehen, dass Objekte im Speicher nicht mehr dupliziert werden müssten, um sie per "return" zurückzugeben (also quasi call per value während des return-prozesses).
Bei grossen Objekten müsste sich das in einen Performance-Vorteil äussern, der dazu noch den Speicher-Verbrauch schont.

Ich habs jetzt einfach mal getestet und es scheint tatsächlich einwandfrei zu funktionieren.
Aber vielleicht gibt es ja Gründe, die ich nicht bedachte habe oder die mir schlicht nicht bekannt sind, weshalb man das nicht machen darf oder sollte.

Was haltet ihr davon?

Hier den konkreten Beispiel-Code:

Gruss starcow

Code: Alles auswählen

#include <iostream>

class Creature {
private:
	int health;
public:
	const int& Health;
	Creature(int);
	~Creature();
	void Increment();
};

Creature::Creature(int health)
: health(health), Health(this->health) {
}

Creature::~Creature() {
}

void Creature::Increment() {
	++health;

	return;
}

int main(int argc, char** Argv) {
	Creature creature(13);

	std::cout << "health: " << creature.Health << std::endl; // Das funktioniert. Health kann so über einen, quasi unveränderten Syntax, ausgegeben werden.
	creature.Increment();
	std::cout << "health: " << creature.Health << std::endl;

	// creature.health = 5; // Das funktioniert nicht, da health private ist.
	// creature.Health = 5; // Das funktioniert ebenfalls nicht, da die Referenz als const definiert wurde.
}
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von Schrompf »

Kann man so machen. Die Referenz kostet halt aber Speicher, ein Getter nicht. Getter geben eh zumeist eine const Referenz und keine Kopie zurück, daher entfällt auch die Kopievermeidung. Und mit einer Referenz in der Klasse zwingst Du Dich dazu, Kopier- und Move-Operationen von Hand zu implementieren. Auch nicht so golden.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Read-only für Klassen-Variablen?

Beitrag von starcow »

Vielen Danke Schrompf! Wieder was gelernt! :-)

Unter Kopier-Operationen stell ich mir jetzt die Problematik vor, dass ich nicht mehr ohne weiteres sowas wie das hier machen darf:

Creature a(13);
Creature b(8);
a = b;

Die Referenz ist ja schliesslich als const definiert und der Versuch, über sie den Wert von a.health zu ändern, muss gezwungenermassen scheitern.
Quasi: a.Health = b.Health.
Die ganze Situation müsste ja von ihrem Wesen her der Thematik rund um die tiefen- und flachen Kopien sehr nahe kommen.
Also muss ich in einem allfälligen Kopierkonstruktor die Referenz von einer Zuweisung ausschliessen.
Stimmt diese Überlegung?

Was muss ich mir unter einer Move-Operation vorstellen?

Gruss starcow
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Antworten