Dr. Erhard Henkes - Stand 06.02.2010

Zur Hauptseite

CString

Im Gegensatz zu den meisten MFC-Klassen, die in der Vererbungshierarchie der Klassen auf CObject ba­sie­ren, besitzt CString keine Oberklasse, sondern steht für sich alleine. CString gehört zu den so­ge­nannten Simple Value Typen innerhalb der MFC-Familie. Weitere Simple Value Typen sind CPoint, CRect, CSize, CTime und CTimeSpan.

CString verwendet als Grundlage den universellen Datentyp TCHAR. Wenn die Benutzung von Unicode fest­ge­legt wurde, entspricht TCHAR dem Datentyp wchar_t  (zwei Byte pro Zeichen). Ist dies nicht der Fall, dann entspricht TCHAR dem klassischen char (ein Byte pro Zeichen).

Betrachten wir die Anweisung

CString strZeichenfolge = _T("Hallo");

Der Compiler wandelt in eine Unicode-Zeichenfolge um, wenn _UNICODE definiert ist, andernfalls in eine ANSI-Zeichenfolge. Das Makro _T wandelt das Zeichenfolgenliteral "Hallo" in die Form L"Hallo" um. Der Compiler sieht diesen String dann als eine Unicode-Zeichenfolge an.

In der Datei  tchar.h  findet man hierzu diesen Sourcecode:

#ifdef _UNICODE
...
#define __T(x) L ## x
...
#else /* ndef _UNICODE */
...
#define __T(x) x
...
#define _T(x) __T(x)
#define _TEXT(x) __T(x)

(Anmerkung: ## ist der "Token-Einfügungsoperator" oder "Vereinigungsoperator")

Im Gegensatz zu den klassischen char-Arrays hat ein Objekt der Klasse CString die Möglichkeitkeit, nach Bedarf Speicher zu reservieren. Man muss also nicht darauf achten, dass ein CString-Objekt entsprechend verlängert wird. Das erfolgt automatisch. Ein Objekt der Klasse CString speichert maximal INT_MAX = 2.147.483.647 Zeichen. Das sollte für übliche Anwendungen genügen.

Damit wir uns hier auf die MFC-Klasse CString konzentrieren können, verwenden wir nachfolgend Kon­so­len­anwendungen. Man bindet hierzu lediglich die Datei afx.h in das Programm ein. Probieren wir es aus:

#include <afx.h>
#include <iostream >
using namespace std;

int main()
{
    CString strZeichenfolge1 = _T("Hallo"); 
    CString strZeichenfolge2( _T("Welt!") );
    CString strZeichenfolge3 = strZeichenfolge1 + _T(" ") + strZeichenfolge2;
    cout << LPCTSTR ( strZeichenfolge3 ) << endl;
 
    strZeichenfolge3.MakeReverse();
    cout << "Invertiert: " << LPCTSTR ( strZeichenfolge3 ) << endl; 
 
    strZeichenfolge3.MakeReverse();
    cout << "Erneut invertiert: " << LPCTSTR ( strZeichenfolge3 ) << endl; 
 
    cout << "Laenge der Zeichenfolge: " << strZeichenfolge3.GetLength() << endl;
    cout << "Siebter Buchstabe: " << strZeichenfolge3.GetAt(6) << endl;
    cout << "Die 3 ersten Buchstaben: " << LPCTSTR( strZeichenfolge3.Left(3) ) << endl;
    cout << "Die 3 letzen Buchstaben: " << LPCTSTR( strZeichenfolge3.Right(3) ) << endl;
    cout << "Die 5 mittleren Buchstaben: " << LPCTSTR( strZeichenfolge3.Mid(3,5) ) << endl;
 
    CString strZeichenfolge4(_T("schoene neue "));
    for (int i=0; i<13; ++i) strZeichenfolge3.Insert(i+6, strZeichenfolge4[i]);
    cout << "Erweiterte Zeichenfolge: " << LPCTSTR( strZeichenfolge3 ) << endl;
 
    while (cin.get() != '\n'); // Damit die Konsole bis zur Eingabe von Enter sichtbar bleibt
 
  return 0;
}

So kommt das klassische "Hallo Welt!", die Konsole und die MFC zusammen. Man kann ein Objekt der Klasse CString über den Zuweisungs-Operator oder über einen Konstruktor initialisieren. Das Aneinanderhängen von CString-Objekten erfolgt einfach durch den Pluszeichen-Operator.

 
Damit die Ausgabe mittels cout problemlos klappt, müssen wir das CString-Objekt zunächst in ein character-Array umwandeln.
Dies erfolgt mit dem Operator LPCTSTR(...). Hierbei ist LPCTSTR (Long Pointer to a Constant String; das T deutet auf die Abhängigkeit von _UNICODE hin) nichts anderes als eine andere Bezeichnung für das in C übliche  const char * bzw. const wchar *, also ein Zeiger auf einen char- oder wchar-String, je nachdem, wie es definiert ist. Bezüglich Unicode finden Sie z.B. hier einen Einstieg und weitere Informationen.

 
Die Klasse CString verfügt über eine Reihe nützlicher Member-Funktionen:

CString::GetLength( ) liefert die Länge der Zeichenfolge als int.

CString::GetAt( int index ) liefert den Buchstaben an der Stelle index+1 der Zeichenfolge als TCHAR, da die Zählweise bei 0 beginnt.

CString::MakeReverse( ) kehrt die Zeichenfolge um. Interessant für Personen, die gerne rückwärts lesen.

Wer die Programmiersprache BASIC kennt, der findet hier sicher erfreut die Analoga zu LEFT$, RIGHT$ und MID$:

CString::Left( int nCount )

CString::Right( int nCount )

CString::Mid( int nFirst ) bzw.

CString::Mid( int nFirst, int nCount )

Beachten Sie bitte, dass der erste Parameter bei Mid(...) in der Zählweise null-basiert ist.

 

Ein Objekt der Klasse CString sollte man sich nicht als Zeiger auf das erste Zeichen eines Strings, sondern als richtiges String-Array vorstellen.

Dies wird durch die zeichenweise Einfügung des Strings "schoene neue " an der i+6-ten Stelle im "Array" deutlich.

 
Der Operator CString::operator[ ] steht hier stellvertretend für CString::GetAt(...). Diese Schreibweise ist analog zum Ansprechen eines Elementes in einem Array.

 
Wie Sie sehen, ist der Umgang mit dem MFC-Typ CString relativ unkompliziert und intuitiv. Versuchen Sie in der Windows-Programmierung mit MFC komplett auf diesen Typ umzusteigen, damit Sie sich die mit character-Arrays verbundenen Probleme ersparen.

 
Einen kompletten Überblick über die verschiedenen Member-Funktionen der MFC-Klasse CString erhält man in MSDN. Wer sich intensiv mit dem inneren Mechanismus von CString auseinander setzen will, findet den Sourcecode in folgenden Dateien:

afx.h (Klassendefinition), strcore.cpp und strex.cpp (MFC Sourcecode)

 
Seit der Version MFC 4.0 verfügt die Klasse CString über einen sogenannten Referenzzähler. Mit dieser Technik werden unnötige Kopien beim Erstellen und Zuordnen vermieden. Man verringert auf diese Weise den Speicherbedarf und erhöht die Geschwindigkeit. Dieser Mechanismus greift z.B. bei folgendem Code:

CString str1 ("Hallo Welt!");
CString str2 = str1;

 
In den beiden Variablen str1 und str2 steht der gleiche Text. Solange keiner der Strings durch Schreiben ver­än­dert werden muss, wird die von allen Stringvariablen genutzte Zeichenfolge "Hallo Welt!" nur an einer Stelle ge­speichert. Soll nun str1 verändert werden, so wird hinsichtlich der Variable str2 reagiert, indem eine Kopie von "Hallo Welt!" erstellt und der interne Referenzzähler (in den oben genannten MFC-Files findet man ihn als Va­ri­able CStringData::nRefs) um eins erniedrigt wird. Diese Referenzzähler-Methode finden Sie auch bei  COM.

 
Wir haben nun Zeichenfolgen erstellt, zugeordnet und auf diese zugegriffen. Man kann Zeichenfolgen auch auf einfachste Weise miteinander vergleichen. Hierzu verwendet man die Operatoren <, <=, ==, >=, > und !=.  Als Ergebnis erhält man TRUE oder FALSE zurück. Nachstehend finden Sie einen solchen Größenvergleich:

#include <afx.h>
#include <iostream>
using namespace std;
 
CString maxString( CString& A, CString& B )
{
    if (A >= B) return A; // hier erfolgt der Vergleich
    else return B;
}
 
CString minString( CString& A, CString& B )
{
    if (A <= B) return A; // hier erfolgt der Vergleich
    else return B;
}
 
int main()
{
    char szString1[255]; // char-Array-String 
    char szString2[255]; // char-Array-String 
 
    cout << "Geben Sie bitte die erste Zeichenfolge ein: ";
    cin.getline( szString1, 255 );
 
    cout << "Geben Sie bitte die zweite Zeichenfolge ein: ";
    cin.getline( szString2, 255 );
 
    CString str1 = _T(szString1);  
    CString str2 = _T(szString2); 
 
    cout << "Der String \"" << LPCTSTR ( maxString( str1, str2 ) ) << 
            "\" folgt nach dem String \"" <<
            LPCTSTR ( minString( str1, str2 ) ) << "\"" << endl;
    
    while (cin.get() != '\n');
    return 0;
}

In vorstehendem Beispiel sieht man auch den Einsatz selbst geschriebener Funktionen namens minString(...) und maxString(...), die Referenzen auf CString als Parameter übernehmen und die 'kleinere' oder 'größere' CString-Variable zurückgeben.

Das Vergleichen  zweier Strings auf Gleichheit erfolgt mit den Funktionen

int Compare( LPCTSTR lpsz )
int CompareNoCase( LPCTSTR lpsz )

Eine hilfreiche Funktion ist CString::Format(...). Die String-Ausgabe wird analog der Funktion sprintf(...), die der Programmiersprache C entstammt, formatiert. Sie sehen an diesem Beispiel, wie die MFC noch auf typische Eigenschaften der Sprache C zurückgreift. Solche historisch gewachsenen Im­ple­men­tie­run­gen findet man in der Windows-Programmierung mit der Sprache C++ immer wieder.

Bevor wir die Verwendung lange theoretisch erläutern, schauen wir uns ein einfaches Beispiel an:

#include <afxwin.h>
#include <iostream>
using namespace std;

int main()
{
  int z[10];
  for(int i=0; i<10; ++i) {z[i] = i+1;}
  CString Ausgabe;
  CString Hilfsstring( _T("Die ersten natuerlichen Zahlen sind:") );
  Ausgabe.Format( "%s %d %d %d %d %d %d %d %d %d %d.\n",
  Hilfsstring, z[0],z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9] );
  cout << LPCTSTR(Ausgabe) << endl;
  AfxMessageBox( Ausgabe, MB_OK | MB_ICONINFORMATION );
  return 0;
}

Diese Technik werden Sie häufig benutzen, wenn Sie Zeichenfolgen und Zahlenwerte als kombinierten String z.B. in einer MessageBox ausgeben oder in eine Datei schreiben wollen. Wir binden in dem Beispiel nicht nur die Datei afx.h, sondern die umfassendere Datei afxwin.h ein, damit wir sowohl den Variablentyp CString als auch die "klassenlose" Funktion AfxMessageBox(...) verwenden können.

Die MFC-Klasse CString bietet auch gut geeignete Funktionen zum Durchsuchen von Zeichenfolgen nach anderen Zeichenfolgen. Man hat die Funktionen

int Find( TCHAR ch )
 
int Find( TCHAR ch, int nStart )

 
int Find( LPCTSTR lpszSub )
                  
 
int Find( LPCTSTR pstr, int nStart )

 
int FindOneOf( LPCTSTR lpszCharSet )
                
 
int ReverseFind( TCHAR ch )

zur Verfügung.

Wir schauen uns zur Veranschaulichung ein kleines Beispiel an:

#include <afx.h>
#include <iostream>
using namespace std;
int main()
{
  CString Glocke =
  "Fest gemauert in der Erden Steht die Form, aus Lehm gebrannt. Heute muß die Glocke werden.";
  cout << Glocke.Find( "Erden" ) << endl;        // Resultat: 21
  cout << Glocke.Find( "Erden", 10 ) << endl;    // Resultat: 21
  cout << Glocke.Find( "Erden", 25 ) << endl;    // Resultat: -1
  cout << Glocke.Find( 'E' ) << endl;            // Resultat: 21
 
  CString S = "E";
  const char *pS = LPCTSTR( S );
 
  cout << Glocke.Find( pS, 0 ) << endl;          // Resultat: 21

  cout << Glocke.Find( 'G' ) << endl;            // Resultat: 76
  cout << Glocke.ReverseFind( 'G' ) << endl;     // Resultat: 76
  cout << Glocke.Find( '.' ) << endl;            // Resultat: 60
  cout << Glocke.ReverseFind( '.' ) << endl;     // Resultat: 89
  cout << Glocke.FindOneOf("EG") << endl;        // Resultat: 21
  cout << Glocke.FindOneOf("GE") << endl;        // Resultat: 21
  cout << Glocke.FindOneOf("ß") << endl;         // Resultat: 70
 
 
return 0;

}



Zur Hauptseite