A
DocShoe schrieb:
In der Borland Hilfe finde ich nirgends Hinweise über Einschränkungen bei der Benutzung von __property, und daher sollte es mit jedem Datentyp gehen. Wenn __property nur für Wertetypen und Zeiger sein soll, dann wäre es ein Leichtes gewesen, das zur compile time zu prüfen und entsprechende Fehler anzuzeigen.
Es geht ja auch (abgesehen von obigem Bug).
DocShoe schrieb:
Natürlich gebe ich Implementationsdetails preis, aber das tue ich mit jeder Property, die Objekte zurückgibt. Selbst wenn ich Zeiger auf Objekte zurückgebe tue ich das, selbst wenn es Zeiger auf Interfaces sind.
Das ist nicht, was ich meinte. Ich bezog mich darauf, daß der Compiler etwas lax ist beim Überprüfen der Zugriffe; das wird oft mehr oder weniger unabsichtlich von Benutzern mißbraucht (z.B. kann man Properties, die für Getter und Setter eine Membervariable verwenden, auch als Referenz verwenden).
Folgender Beispielcode aus einem QC-Report, den ich einmal verfaßt hatte, dürfte das etwas näher erklären:
#include <vcl.h>
#pragma hdrstop
class MyClass
{
private:
Char FChar;
int FInt;
AnsiString FAnsiString;
WideString FWideString;
void __fastcall SetChar (Char Value)
{ FChar = Value; }
void __fastcall SetInt (int Value)
{ FInt = Value; }
int __fastcall GetInt (void)
{ return FInt; }
void __fastcall SetAnsiString (AnsiString Value)
{ FAnsiString = Value; }
void __fastcall SetWideString (WideString Value)
{ FWideString = Value; }
WideString __fastcall GetWideString (void)
{ return FWideString; }
public:
MyClass (void)
: FInt (0), FChar (0)
{}
__property Char MyChar = { read = FChar, write = FChar };
__property int MyInt = { read = GetInt, write = SetInt };
__property AnsiString MyAnsiString = { read = FAnsiString, write = SetAnsiString };
__property WideString MyWideString = { read = GetWideString, write = SetWideString };
};
void foo (Char& i)
{ i = 'A'; }
int main (void)
{
MyClass mc;
// Case 1: works as expected.
// Code is translated to mc.SetInt (mc.GetInt () + 2);
mc.MyInt += 2;
// Case 2: works, but relies on implementation detail.
// Code is translated to foo (mc.FChar);
foo (mc.MyChar);
// Case 3: accesses private field directly although property
// definition specifies a setter method.
// Code is translated to mc.FAnsiString.sprintf ("test");
// mc.FAnsiString += "concat";
mc.MyAnsiString.sprintf ("test");
mc.MyAnsiString += "concat";
// Case 4: modifies temporary variable returned by GetMyUnicodeString
// and therefore has no effect.
// Code is translated to mc.GetWideString ().sprintf ("test");
mc.MyWideString.sprintf (L"test");
}
Wenn du tatsächlich einfach nur eine Variable öffentlich sichtbar machen willst, dann würde ich nicht ein Property verwenden, sondern sie als public deklarieren. Ein Property bringt dir zumindest in deinem Falle keinen Vorteil, da er dich auf deine Festlegung von Setter und Getter fixiert; bei Code wie t.V.push_back (2); hängen die Auswirkungen, wie mein Codeausschnitt erklärt, davon ab, wie du Setter und Getter definiert hast.
In Delphi sind Properties in jedem Kontext außer der direkten Zuweisung konstante R-Values; so sollte das eigentlich auch in C++Builder sein. Genau dieses Thema wurde aber in o.g. Thread bereits erschöpfend diskutiert; du kannst ja mal nachlesen, sobald der Newsserver wieder verfügbar ist.
DocShoe schrieb:
In der VCL gibt es zuhauf Zeiger auf TStringList (oder deren Interfaceklasse TStrings) , die per __property zurückgegeben werden und per AddString() modifiziert werden können. Um das Dilemma in den Griff zu bekommen benutzt Borland das OnChange Ereignis, damit die umgebende Klasse Änderungen an der StringList mitbekommt. Das ist IMHO ein Workaround für dieses Problem, wenn man das wirklich sauber lösen möchte muss man wohl für jede nutzbare TStringList Methode eine Proxy Methode in der umgebenden Klasse bereitstellen, was natürlich total blödsinnig weil zu aufwändig ist. Daher sehe ich es als durchaus sinnvoll an, Referenzen auf member zurückzugeben.
Sehe ich ebenso.
DocShoe schrieb:
Ausserdem führen Objektkopien bei grossen Objekten zu Performance Hits, z.B. wenn ein deque 100.000 Elemente hat, die bei jedem getter Aufruf kopiert werden müsste.
Das sagte ich doch.
audacia schrieb:
Properties sind nur für die Rückgabe von Wertetypen gedacht (worunter auch Zeiger fallen). Fragwürdigerweise ist zwar auch std::vector<> ein solcher, und deshalb funktioniert das auch, aber die Verwendung einer Getter-Funktion führt in der Regel zur Erstellung einer Kopie, und das ist nur bei tatsächlichen Wertetypen unproblematisch.
Eine Beschränkung auf Wertetypen bedeutet für einen C++-Programmierer nichts, denn in C++ gibt es überhaupt nur Wertetypen (eine Ausnahme in C++Builder sind Delphi-Klassen, die eben nur per Zeiger übergeben werden können). Mit "tatsächlichen Wertetypen" meine ich solche, bei denen die Kopiersemantik naheliegend und gut begründet ist. Container gehören da IMHO nicht dazu.