003
09.03.2006, 13:04 Uhr
ao
(Operator)
|
Ich verändere mal das Beispiel und "verCPlusPlusse" es ein bisschen, dann sieht mans besser, und da ich nicht weiß, was "String" ist, nehme ich std::string.
C++: |
#include <string> #include <iostream>
class Employee { private: std::string itsName; long itsSalary;
public: Employee(const std::string & rName, long salary) : itsName (rName) , itsSalary (salary) { }
const std::string & Name () const { return itsName; } // name as rvalue only -> name cannot be changed.
long Salary () const { return itsSalary; } // salary as rvalue -> this returns the value long & Salary () { return itsSalary; } // salary as lvalue -> this returns a reference and // lets the value be changed. };
int main (void) { Employee e ("Dilbert", 10000);
std::cout << e.Name () << " earns $" << e.Salary () << std::endl; // Name and Salary used as rvalues
std::cout << e.Name () << " gets a raise." << std::endl; e.Salary () = 12000; // Salary used as lvalue
std::cout << e.Name () << " earns $" << e.Salary () << std::endl;
return 0; }
|
Output:
Code: |
Dilbert earns $10000 Dilbert gets a raise. Dilbert earns $12000
|
Erklärungen:
Im Konstruktor sorgt die const-string-Referenz dafür, dass nicht nur echte std::string-Objekte reingereicht werden können
C++: |
std::string name = "Seppel"; Employee e (name, 100);
|
sondern auch alle Datentypen, aus denen ein const-string konstruiert werden kann, zum Beispiel String-Literale
C++: |
Employee e ("Hans Mustermann, 1000);
|
Char-Pointer, Funktionsergebnisse, die char* oder string sind und so weiter. Das macht diesen Konstruktor flexibel, und man muss nicht für jeden dieser Quell-Typen einen eigenen Konstruktor bauen, sondern der eine erschlägt alles. Mit einer Nicht-const-Referenz würde das nicht gehen.
In der Funktion Name () wird das angebundene Employee-Objekt als unveränderlich definiert. Name () kann also nur in Kontexten verwendet werden, in denen der Name abgefragt und nicht neu gesetzt wird (sogenannter "rvalue-Kontext"). Jeder Versuch, etwas wie
C++: |
e.Name () = "Wally";
|
zu machen, führt zum Compilerfehler. Aus Performance-Gründen (Strings können lang sein!) liefert Name () keine Kopie des Wertes, sondern eine Referenz. Diese muss auch wieder const sein, damit sie zu itsName passt: Innerhalb der "Name () const"-Funktion sind alle Member des angebundenen Employee-Objekts implizit const.
Für die erste Version der Funktion Salary () gilt dasselbe, nur auf die Referenz aus Performance-Gründen kann verzichtet werden und stattdessen eine Kopie geliefert werden. Die zweite Version liefert eine Referenz auf itsSalary, und sie trägt kein const-Attribut. Über diese Funktion kann itsSalary verändert werden ("lvalue-Kontext").
Auf diese Weise kann man festlegen, was mit einer Klasse angestellt werden darf und was nicht. Der Benutzer der Klasse kann so nur die vom Entwickler vorgesehenen Aktionen programmieren, es sei denn, er castet irgendwelche const-Modifier mit brutaler Gewalt weg. Dann ist er aber auch selber verantwortlich.
ao |