Grundsätzlich: Strings als Übergebeparameter
-
Hallo,
Ich quäle mich zur Zeit mit der Frage, welche Variante sinnvoller ist, ein String als Parameter an eine Methode zu übergeben.
Angenommen, man möchte Attribute von Objekten setzten, wie z.B. ein Pfad zu einer Datei etc., so könnte man folgende Varianten verwenden (ich benutze mal die UNICODE-Varianten):
class Config{ public: Config(); ~Config(); void SetFilename1(LPCWSTR fileName); void SetFilename2(const std::wstring& fileName); void SetFilename3(const std::wstring fileName); private: std::wstring cfgFileName; } void Config::SetFilename1(LPCWSTR fileName){ cfgFileName = fileName; } void Config::SetFilename2(const std::wstring& fileName){ cfgFileName = fileName; } void Config::SetFilename2(const std::wstring fileName){ cfgFileName = fileName; }Welche Variante würdet Ihr nehmen (eine von den obigen oder eine weitere Variante)?
Gruß Roger
-
Roger Wilco schrieb:
void SetFilename2(const std::wstring& fileName);die da, weil sie keine unnötigen kopien erzeugt.
-
Ich würde const reference übergeben.
Grund
v1) was ist ein LPCWSTR? (wstring ist glaub ich so ein windows-unicode-string, oder?)
v2) übergibt nur eine Referenz auf einen String, per Zuweisungsoperator setzt du einmal den Wert.
v3) Du übergibst eine Kopie eines wstrings, dann weist du den Wert per operator= zu, macht also 2x Werte kopieren, im Gegensatz zu 1x bei v2.Grüße
Franz
-
franz schrieb:
v1) was ist ein LPCWSTR? (wstring ist glaub ich so ein windows-unicode-string, oder?)
Long Pointer to Constant Wide STRing. elendes windows-geraffel.
-
alfonso schrieb:
franz schrieb:
v1) was ist ein LPCWSTR? (wstring ist glaub ich so ein windows-unicode-string, oder?)
Long Pointer to Constant Wide STRing. elendes windows-geraffel.
Danke, gut zu wissen

Aus dem Namen geht hervor, dass das wohl ein Pointer ist, somit wär das vllt. auch eine Alternative, da bei der Parameterübergabe an die Funktion kein Kopieren statfindet.
-
franz schrieb:
somit wär das vllt. auch eine Alternative, da bei der Parameterübergabe an die Funktion kein Kopieren statfindet.
Nur wenn du in der Schnittstelle der Klasse sowieso noch andere Windows-Spezifika einbauen musst, d.h. wenn es sowieso eine Windows-spezifische Klasse ist. Allgemein würde ich wenn möglich die Schnittstellen von Klassen möglichst plattformunabhängig definieren.
-
@Franz: Zum LPWSTR (Zeiger auf ein Unicode C-String: wchar_t*, LPCWSTR ist die const-Variante)
Diesen Datentyp trifft man bei der WinAPI so gut wie immer an, wenn es um String-Parameter geht.
Bei meinen Klassen handelt sich um stark WinAPI-bezogene Klassen für Windows-BS. Ich nutze aber intern immer std::wstring. Nun überlege ich auch für die Schnittstellen grundsätzlich auf std::wstring zu setzen. Bisher habe ich es mit LPCWSTR gemacht, was den Nachteil hat, dass man immer wieder auf C-Strings zurückgreifen muss (z.b: per .c_str()).
Die Variante 3 nutze ich dort, wo ich die Option benötige wstrings und const. C-Strings (z.B. SetFilename(L"config.ini");) übergeben zu können.
-
Roger Wilco schrieb:
Die Variante 3 nutze ich dort, wo ich die Option benötige wstrings und const. C-Strings (z.B. SetFilename(L"config.ini");) übergeben zu können.
Wieso das? Const-Referenzen können auch an temporäre Objekte gebunden werden. Übergib doch eine Const-Referenz.
-
@Nexus: Das musst Du mir nochmal erklären.
-
Roger Wilco schrieb:
@Nexus: Das musst Du mir nochmal erklären.
funktion 2 funktioniert auch mit c-strings, weil ggf. ein temporäres stringobjekt erzeugt wird.
-
Roger Wilco schrieb:
@Nexus: Das musst Du mir nochmal erklären.
Es gibt eigentlich keinen Grund, Strings (wie
std::wstring) als Kopie zu übergeben. Das einzige wäre, wenn du innerhalb der Funktion sowieso eine Kopie anlegen würdest, und diese veränderst. Dann kannst du gleich eine Kopie übergeben. Sonst nimmst du besser eine Const-Referenz.Auch wenn du ein Literal an die Funktion übergeben willst, zum Beispiel
L"config.ini", darf der Parameter eine Const-Referenz sein. Normale Referenzen können nur an LValues gebunden werden. Const-Referenzen hingegen darf man auch an RValues (zum Beispiel temporäre Objekte wie das WString-Literal) binden.
-
alfonso schrieb:
funktion 2 funktioniert auch mit c-strings, weil ggf. ein temporäres stringobjekt erzeugt wird.
Das ist das andere.
std::wstringbesitzt einen nicht expliziten Konstruktor, der einenwchar_t*aufnimmt. Also kann man auch einenwchar_t*übergeben, der wird dann implizit konvertiert.Im Grunde braucht man damit nur eine Funktion, mit der alle Fälle abgedeckt werden.
-
Getestet und funktioniert.
Danke, wieder etwas dazu gelernt. 
Würdet Ihr im Falle der Windows-Programmierung, wo man viel mit LPCSTR/LPCWSTR als Parametertyp zu tun hat und intern mit std::wstring arbeitet, für eigene Schnittstellen die C-String-Zeiger (LPCWSTR) oder C++-String-Referenzen (const std::wstring&)benutzen?
-
Roger Wilco schrieb:
Würdet Ihr im Falle der Windows-Programmierung, wo man viel mit LPCSTR/LPCWSTR als Parametertyp zu tun hat und intern mit std::wstring arbeitet, für eigene Schnittstellen die C-String-Zeiger (LPCWSTR) oder C++-String-Referenzen (const std::wstring&)benutzen?
Wie schon oben gesagt würde ich grundsätzlich plattformunabhängige Schnittstellen bereitstellen. Die einzige Ausnahme ist, wenn du die Schnittstellen der WinAPI übergeben musst, damit diese die Funktionen aufrufen kann. Da die WinAPI aber ein C-API ist wirst du dafür sowieso nicht die Klassenmethoden direkt benutzen können.
Der Vorteil bei plattformunabhängigen Schnittstellen ist folgender: Auch wenn du im Moment die Implementierungen deiner Klassen stark windows-spezifisch machst, kannst du später trotzdem z.B. eine Linuxspezifische Implementierung dazu schreiben, die du dann mit linken kannst, ohne die Schnittstelle selber ändern zu müssen.
-
Danke pumuckl, Recht hast Du.
Ich denke auch std::strings als const. reference zu benutzen ist auch einfach fehlersicherer. Die LPCWSTR-Zeiger können ja sonst wo hinzeigen (auch NULL sein) und man kann sich nicht sicher sein, dass die Strings auch initialisiert und nullterminiert sind.
-
Roger Wilco schrieb:
Ich denke auch std::strings als const. reference zu benutzen ist auch einfach fehlersicherer. Die LPCWSTR-Zeiger können ja sonst wo hinzeigen (auch NULL sein) und man kann sich nicht sicher sein, dass die Strings auch initialisiert und nullterminiert sind.
Wenn du aber einen
wchar_t*übergibst, wird der in einenstd::wstringkonvertiert - egal, ob er auf einen gültigen Speicherbereich, auf Null oder in ein schwarzes Loch zeigt.std::wstringwird dann die entsprechenden Probleme haben...Wenn es also möglich ist, Nullzeiger zu übergeben, brauchst du doch eine Funktion, die einen
wchar_t*nimmt und diesen prüft. Gegen wilde Zeiger kannst du nicht viel machen, da musst du halt schauen, dass diese nicht vorkommen, was bei guter Programmierung eigentlich im Bereich des Möglichen liegt.
-
Nexus schrieb:
Wenn es also möglich ist, Nullzeiger zu übergeben, brauchst du doch eine Funktion, die einen
wchar_t*nimmt und diesen prüft. Gegen wilde Zeiger kannst du nicht viel machen, da musst du halt schauen, dass diese nicht vorkommen, was bei guter Programmierung eigentlich im Bereich des Möglichen liegt.Das ist eine Frage des Schnittstellendesigns. Tendenziell würde ich die Schnittstelle eher schmal halten, sprich nur std::wstring. Es ist dann Sache des Aufrufers bei eventuell auftretenden wchar_t-Pointern die nötigen Überprüfungen vorzunehmen. Idealerweise sollte eine Schnittstelle nur die nötigen Funktionen bereitstellen und nicht alle möglicherweise irgendwann mal auftretenden Fälle abdecken. In der Praxis macht man natürlich Kompromisse - wenn ein Fall häufig auftaucht, der von der minimalen Schnittstelle nicht abgedeckt wird, dann erweitert man die Schnittstelle unter Umständen entsprechend. Im vorliegenden Fall würde ich aber eher drauf verzichten und lediglich in die Doku der Schnittstelle schreiben, dass die wstrings gewissen Einschränkungen unterliegen (z.B. keine wchar_t-Nullpointer).
-
Kommt ja auch eher darauf an, was man entwickelt. Wenn ich eine Library (besonders für andere Projekte) entwickle, wird meine Schnittstelle Fälle voraussehen müssen. Damit auch mehr Projekte damit etwas anfangen können.
Wenn es eher um Schnittstellen für geschlossene Systeme geht (z.B. innerhalb einer Anwendung), dann mache ich nur die nötigsten Schnittstellen. Weil wenn ich was brauche, kann ich die ruck zuck selber hinzufügen.
-
@pumuckl: Stimmt...

@all: Und welche Variante bevorzugt ihr für Strings als Rückgabewert?
std::wstring GetFilename(); std::wstring configFile = GetFilename();bool GetFilename(std::wstring& filename); std::wstring configFile; if (!GetFilename(configFile)){ ... }
-
variante 1.
welchen sinn sollte variante 2 haben?