CSharp Zu CPP Kurzreferenzen
| Umstieg von C# nach C++ | |
|---|---|
| Kurzreferenzen | |
| Autor | Kevin |
| Programmiersprache | C#, C++ |
| Kategorie | C#-Tutorials, C++-Tutorials |
| Diskussion | Thread im Forum |
| Lizenz | indiedev article license |
Einige Leute hier werden vor allem bereits Erfahrung in C# haben und dann kann der Umstieg auf C++ an manchen Stellen etwas knifflig sein. Dieser Artikel dient dazu grundlegende Konzepte aus C# nach C++ zu übertragen. Es wird dabei an manchen Stellen die C++-Standardbibliothek verwendet, statt die Klassen selbst zu implementieren. Teilweise ist es natürlich Geschmackssache, ob man die Standardbibliotheken verwendet oder es selbst implementiert, aber der Einfachheit halber wird sie in diesem Artikel genutzt werden.
Der Artikel richtet sich vor allem an bereits erfahrene C# Programmierer, die sich C++ aneignen wollen. Es wird hierbei stets zuerst der C# Code vorgestellt und anschließend der dazugehörige C++ Code.
Inhaltsverzeichnis |
System.Collection.Generic.List<T>
Die gängigste Container Art in C# ist die List, welche vor allem dazu verwendet werden kann dynamisch Objekte hinzuzufügen im Gegensatz zum Array.
using System.Collections.Generic; //... List<int> myList = new List<int>(); myList.Add(22); int z = myList[0];
Zwar bietet C++ ebenfalls eine verkettete Liste std::list<T>, jedoch ist hier ein Zugriff an der nten Stelle nicht möglich, es kann lediglich über einen Iterator auf die Objekte zugegriffen werden, daher eignet sich die std::vector<T> Klasse für unseren Zweck eher:
#include <vector> using namespace std; //... vector<int> myVector; myVector.push_back(22); //Das Element am Ende des vectors hinzufügen int z = myVector[0];
System.Collection.Generic.Dictionary<TKey,TValue>
Dictionaries dienen dazu mehrere Key-Value Paare zu speichern und über die Keys auf die Values zuzugreifen.
using System.Collections.Generic;
//...
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
myDictionary.Add("Zahl1", 42);
int z = myDictionary["Zahl1"];
Um dies in C++ umzusetzen verwendet man eine std::map<TKey,TValue>, welche sehr ähnlich funktioniert:
#include <map>
using namespace std;
//...
map<string, int> myMap;
myMap.insert(map<string, int>::value_type("Zahl1", 42));
int z1 = myMap["Zahl1"];
Folgendes ist hier jedoch zu beachten: Der TValue Typ muss einen Konstruktor ohne Parameter besitzen, denn wenn man nun folgendes aufruft:
int z2 = myMap["Zahl2"];
wird der map ein neues Objekt hinzugefügt, hierfür wird der Default Konstruktor aufgerufen und in diesem Fall hat z2 deshalb den Wert 0. Weitere Informationen finden sichhier.
Var
Oftmals will man sich Arbeit sparen und nimmt statt dem eigentlichen Typ eines Objekts einfach var:
var v = new ParticleSystem();
Glücklicherweise gibt es sowas auch in C++, was sich besonders bei komplexen Typnamen lohnt:
auto v = new ParticleSystem();
auto ist ein Feature des C++2011 Standards. Dieses ist in Visual Studio ab Version 2010 und in g++ seit Version 4.4 verfügbar.
foreach Schleife
Bei Containern lohnt sich oftmals die Verwendung der foreach Schleife, die enorm komfortabel zu benutzen ist:
List<int> myList = new List<int>(); //... foreach (int elm in myList) Console.WriteLine(elm);
In C++ lässt sich dies sehr ähnlich umsetzen:
vector<int> myVector; for each(int v in myVector) cout<<v;
Properties
Zum guten Programmierstil in C# gehörte die Nutzung von Properties statt public Variablen:
public class Terrain
{
private float width;
public float Width
{
get
{
return width;
}
set
{
if(value <= 2048 && value > 0)
width = value;
}
}
}
//...
Terrain terrain = new Terrain();
terrain.Width = 256.0f;
Ín C++ würde man dies folgendermaßen implementieren:
class Terrain {
public:
float getWidth() { return width; }
void setWidth(float value) { if(value <= 2048 && value > 0) width = value; }
private:
float width;
};
Allerdings ist das Zugreifen auf diese Variable über die Methodenaufrufe recht "unschön"(was natürlich Geschmackssache ist), deshalb kann man folgende Zeile hinzufügen, um eine Property genauso wie in C# zu verwenden:
//In der Terrain Klasse im public Bereich... __declspec(property(get = getWidth, put = setWidth)) float Width; //... Terrain* terrain = new Terrain(); terrain->Width = 256.0f; float width = terrain->Width;
Anzumerken ist jedoch noch, dass diese Variante Microsoft spezifisch ist und lediglich auf Microsoft Compilern funktionieren wird. Es bietet sich deshalb auch an, dass man eigene Macros dafür definiert, sodass man es später bei einem Plattformwechsel mit einer eigenen Property Klasse ersetzen kann. Außerdem könnte man es mit Macros noch lesbarer machen. Wer diese "Microsoft Property" nicht verwenden will kann sich auch ran machen und eine eigene Property Klasse implementieren, im Web gibt es dazu einige Artikel.
Generics
Bei Listen und Dictionaries hatten wir bereits Generics, aber wie implementiert man seine eigenen Generic Methoden/Klassen?
Generell kann man um dynamische Typen zu erlauben Methoden (und Klassen) mit Generics austatten:
public T Load<T>(string path)
{
object obj;
//...
return (T)obj;
}
In C++ heißt dies allerdings nicht Generics, sondern Templates und lässt sich folgendermaßen implementieren:
template<class T>
T Load(string s) {
auto obj;
//...
return (T)obj;
}
Auf den ersten Blick sieht es so aus, als seien sich C# Generics und C++ Templates sehr ähnlich, dies stimmt jedoch nicht ganz. Der wichtigste Unterschied ist, dass Generics zur Laufzeit aufgelöst werden und Templates beim kompilieren. Weitere Informationen finden sich hier.
Params: Variable Argumentenlisten
Hin und wieder verwendet man in C# variable Argumentenlisten mittels dem Keyword 'params', um einer Methode beliebig viele Argumente zu übergeben.
public float Sum(params float[] values)
{
float result = 0.0f;
foreach (float f in values)
{
result += f;
}
return result;
}
//...
float sum = Sum(2.0f, 3.0f, 5.0f, 10.0f, 0.5f); //=> 20.5f
Dies geht ebenfalls in C++, auch wenn es hier etwas komplizierter ist:
float Sum(int num, float f1, ...) {
va_list numbers;
va_start(numbers, f1); //Die "Liste" initialisieren, indem sie auf den Wert des ersten Elements gesetzt wird
float res = f1;
for(int i = 0; i < num; i++) {
res += va_arg(numbers, double); //Das nächste Element auslesen
}
va_end(numbers); //Die "Liste" wieder aufräumen
return res;
}
//...
float sum = Sum(5, 2.0f, 3.0f, 5.0f, 10.0f, 0.5f); //=> 20.5f
Wir sehen nun sofort, dass man in C++ ebenfalls die Anzahl der Argumente übergeben muss. Wenn va_arg am Ende angekommen ist liefert es zwar 0 zurück und wir könnten dies zwar als Abbruchbedingung nutzen, doch dann könnten wir unseren Parametern nicht die Zahl 0.0f übergeben, da es sonst alle Zahlen danach auslassen würde. Des Weiteren wird bei va_arg ein double als Typ verwendet und auf den float addiert, das hat damit zu tun, dass die Variablen Argumente einige Probleme mit floats machen und dient als simpler Workaround. Dieses Thema ist relativ komplex und ich würde daher gerne auf diese beiden Links verweisen: Link1 und Link2
System.Threading.Tasks.Parallel.For
Nun zu einem etwas "exotischeren" Anwendungsgebiet...Threading. Seit .Net 4.0 ist es möglich einfache Dinge wie Schleifen über die Parallel Klasse parallel auszuführen, statt sich manuell mit dem Threading rumschlagen zu müssen:
using System.Threading.Tasks;
//...
Parallel.For(0, 100, i =>
{
Console.WriteLine(i);
});
In C++ kann man Nutzen von der kostenlosen OpenMP Bibliothek machen, die bereits in Visual Studio integriert ist und lediglich unter Properties->C/C++->Language->Open MP Support angeschalten werden muss. Anschließend lässt sich das Beispiel ähnlich einfach implementieren:
#include <omp.h>
//...
#pragma omp parallel for
for(int i = 0; i < 100; i++) {
std::cout << i;
}
| |
Merke Dies ist eine seperate Bibliothek, die genauso wie die C++-Standardbibliothek recht umfangreich ist, sich aber perfekt für solche einfachen Threading Tasks eignet. Weitere Informationen finden sich hier und hier. |