| Autor |
Titel |
| Bjarne Stroustrup |
The C++ Programming Language 3. Edition |
| Ulrich Breymann |
C++ Eine Einführung |
| Namir Clement Shammas |
C++ für Dummies |
Wie Programme gestartet werden
Ein kurzer
Überblick über ELF und wie der Linux Kernel Programme startet.
22-09-2012 11.37
export CC=gcc-3.2 CXX=g++-3.2
06-08-2005 13.09
$ ulimit -c unlimited
$ gcc -g PRG
$ g++ -g PRG
$ ./PRG
- CRASH -
$ gdb PRG core
run
bt
up
down
print $VAR
break foo.cpp:301
delete 1
DDD - Data Display Debugger
valgrind
valgrind --leak-check=yes --show-reachable=yes ./MYPROGRAM
memprof
alleyoop
GNU gprof
g++ -g -pg foo.cpp -o foo.run
./run
gprof foo.run gmon.out | less
gprof foo.run gmon.out -f Foo::myfunction | less
doxygen
doxygen (x)emacs
C-c d f
C-c d ;
23-12-2006 12.47
#ifndef _myh1_h_
#define _myh1_h_
#include <iostream>
namespace my_program {
extern int global1;
}
#endif /* _myh1_h_ */
#include <GL/glut.h>
#include "cg3_ex1.h"
using namespace std;
namespace my_program
{
int global1 = 5;
float global2 = 6.0;
/**
* comment here
*/
void foo()
{
}
}
int main(int argc, char **argv)
{
using namespace my_program;
return 0;
}
In C++ meist nicht nötig, vermeiden.
#define FOO blabla
#define BAR(a,b) 1: a 2: b
(kein ; am Ende der Zeile nötig, FOO und BAR werden einfach durch Text rechts ersetzt).
Bestimmte Bereiche nicht kompilieren:
#define VERSION1
...
#ifdef VERSION1
...
#elseif
...
#endif
Klammern setzte nicht vergessen:
#define SQUARE1(a) a*a
#define SQUARE2(a) (a)*(a)
SQUARE1(1+2) // 1+2*1+2
SQUARE2(1+2) // (1+2)*(1+2)
25-10-2006 23.46
int x; x=5; cout << x; // 5
int& r=x; cout << r; // 5
int* y; y=&x; cout << y; // 5 if x is still valid, trouble otherwise
int* y; y=new int; delete y;
int* z; z=y; cout << z // 5
x=*z; cout << x // 5
#include <limits>
#include <iostream>
int main()
{
using namespace std;
numeric_limits<int> INT;
...
cout << "INT Min: " << INT.min() << " Max: " << INT.max() << " EP: " << INT.epsilon() << endl;
...
}
|
int |
uint |
short |
ushort |
long |
ulong |
float |
double |
ldouble |
| min i486 |
-2147483648 |
+0 |
-32768 |
+0 |
-2147483648 |
+0 |
1.17549e-38 |
2.22507e-308 |
3.3621e-4932 |
| min amd64 |
-9223372036854775808 |
| max i486 |
+2147483647 |
+4294967295 |
+32767 |
+65535 |
+2147483647 |
+4294967295 |
3.40282e+38 |
1.79769e+308 |
1.18973e+4932 |
| max amd64 |
+9223372036854775807 |
+18446744073709551615 |
| epsilon i486 |
0 |
1.19209e-07 |
2.22045e-16 |
1.0842e-19 |
| epsilon amd64 |
Catching Integer Overflows in C
Der Wert von x kann nicht geändert werden
const int x=4;
Zeiger zeigt auf einen Wert der nicht geändert werden darf
int const * z;
const int * z;
Zeiger darf nicht geändert werden, der Wert auf den gezeigt wird schon
int * const z;
Zeiger und Wert dürfen nicht geändert werden
int const * const z;
const int * const z;
Methoden die keine Klassenvariablen ändern immer als const kennzeichnen
class Foo {
public:
int getX() const {
return this->x;
}
}
Manchmal möchte man vielleicht doch eine Klassenvariable in einer Methode ändern die eigentlich nichts am Objekt verändert. Zum Beispiel könnte man zum Debuggen zählen wollen wie oft eine get Methode aufgerufen wurde.
class Foo {
unsigned int statistic_access_counter;
public:
int getX() const {
this->statistic_access_counter++; // Error!
return this->x;
}
}
Das wird der Compiler aber nicht zulassen. Um das zu umgehen gibt es zwei Möglichkeiten.
Entweder man benutzt const_cast um vollen Schreibzugriff zu erlangen:
class Foo {
unsigned int statistic_access_counter;
public:
int getX() const {
Foo * tmp=const_cast<Foo *>(this);
tmp->statistic_access_counter++;
return this->x;
}
}
Besser ist allerdings die Klassenvariable zu kennzeichnen die, weil sie keine wichtigen Auswirkung auf das Objekt hat, trotz const geschrieben werden darf:
class Foo {
mutable unsigned int statistic_access_counter;
public:
int getX() const {
this->statistic_access_counter++;
return this->x;
}
}
int * ptr2=(int*)malloc(155*sizeof(int));
for (int i=0; i<155; i++) {
std::cout << ptr2[i];
}
int a[3]; // a[0], a[1], a[2]
int b[]= {10,11,42};
char c[]= {'a','z','m'};
int d[2]={10,11,42}; // Error!
int d[7]={10,11}; // a[2]==a[3]==...==a[6]==0
b={10,11,42}; // Error
Als Beispiel, wie man einen (mit 0 abgeschlossenen) c String kopiert.
mystringcopy(char * a, const char * b) {
// A
while(*b!=0) {
*a=*b;
a++;
b++;
}
*a=0;
// B
while( (*a++=*b++) != 0 );
// C
while( *a++=*b++ );
}
Variante B und C nutzen aus dass die Zuweisung a=b als Rückgabewert b hat.
char c[3];
c[0]='a';
c[1]='b';
c[2]=0;
char * c1=new char[3];
char * c2="ab";
int i=1242;
int len=(int)floor(log(i)/log(10));
char c[16+l+1];
snprintf(c,sizeof(c),"This is my int: %d",i);
c[sizeof(c)-1]=0;
char c[5]="1242";
int i=atoi(c);
char *p, *s;
s = "20y";
int i2 = strtol(s,&p,0); // i2:20 p:y
string str2("abc");
C++ strings
enum { FOO, BAR };
enum myenumtyp { FOO, BAR };
typedef enum { FOO, BAR } myenumtyp;
enum myenumtyp { FOO=0, BAR=1 };
Enumerations sind oft sinnvoller als normale Konstanten weil der Compiler z.B. warnen kann wenn man in einer switch Anweisung nicht alle Möglichkeiten überprüft.
27-01-2007 06.37
Normale Funktion mit Wert Rückgabe und Wertkopie im Parameter:
int foo(int x) {
return x++;
}
Wie oben nur mit Konstanter Referenz als Paramater (schneller)
int foo(const int& x) {
return x++;
}
Übergabe per Referenz, die Variable im Paramater kann in der Funktion geändert werden
void foo(int& x) {
x++;
}
Rückgabe per const Referenz. Wichtig:
Das Ergebnis muss auch in nen Referenzvariable, sonst bekommt man eine Kopie.
int & foo() {
return x;
}
int a=foo(); // :-(
int & b=foo(); // :-)
Zwei normale Funktionen:
int DoIt1 (float a, int b, int c)
{
return a+b+c;
}
int DoIt2 (float a, int b, int c)
{
return a*b*c;
}
class MyClass {
public:
int DoIt3(float a, int b, int c)
{
return a+b*c;
};
};
Zeiger für die Funktionen definieren:
int ( *foo)(float, int, int);
int (MyClass::*bar)(float, int, int)=&MyClass::DoIt3;
Zeiger mit Werten belegen und aufrufen:
foo = DoIt1;
int result1 = foo(1.0, 2, 3);
int result2 = (*foo)(1.0, 2, 3);
foo = DoIt2;
int result3 = foo(1.0, 2, 3);
MyClass * f;
int result4 = f->DoIt3(1.0, 2, 3);
int result5 = (f->*bar)(1.0, 2, 3);
Zeiger als Parameter übergeben:
void myfunc(int (*foo)(float, int, int))
{
int result = foo(1.0, 2, 3);
}
myfunc(&DoIt1);
myfunc(&DoIt2);
void foo(bool a, bool b=false);
Zweiter Parameter optional, default ist false
void foo(bool a ...);
Beliebig viele Argumente vom Type bool (mindestens eins), <cstdarg> um an die Argumente zu kommen.
02-11-2006 21.54
class MyClass : public MyParentClass
{
public:
MyClass();
MyClass(int x);
int my_method() const;
static int getTime();
protected:
int my_var;
private:
};
MyClass::MyClass() {
..
}
int MyClass::MyClass(int x) : MyParentClass(59)
{
...
}
int MyClass::my_method() const {
...
}
int MyClass::getTime() {
...
//in java super()
this is super() for c++
MyParentClass::getTime();
...
}
MyClass x(5);
MyClass * x=new MyClass(5);
Achtung, man braucht
genau eine zusätzliche Definition der statischen Variablen
außerhalb der Klasse.
Wenn man eine statische Methode implementiert nicht das Schlüsselwort static wiederholen.
class MyOtherClass {
public:
MyOtherClass() {
z=0;
}
static int foo() {
return ( MyOtherClass::x + MyOtherClass::y );
}
int bar() {
return ( MyOtherClass::x + MyOtherClass::y + this->z );
}
protected:
static int y;
static int x;
int z;
};
// !!!
int MyOtherClass::x=0;
int MyOtherClass::y=0;
// !!!
int main() {
...
}
Wenn man nicht virtuelle Methoden in abgeleiteten Klassen modifiziert ist man vielleicht überrascht dass hier die Methode der Vaterklasse aufgerufen wird. Übel wenn der Destruktor so eine Methode ist.
#include <iostream>
using namespace std;
class A {
public:
int foo() {
return 1;
}
virtual int bar() {
return 1;
}
//~A() { <------ Don't!
virtual ~A() {
//...
}
};
class B : public A {
public:
int foo() {
return 2;
}
virtual int bar() {
return 2;
}
//~B() {
virtual ~B() {
//..
}
};
int main() {
A a; B b; A * x; x=&b;
cout << a.foo() << a.bar() << endl; // 11
cout << b.foo() << b.bar() << endl; // 22
cout << x->foo() << x->bar() << endl; // 12
return 0;
}
| public: | Alle |
| protected: | Alle Objekte der Klasse + deren Erben |
| private: | Nur Objekte dieser Klasse |
Klassenvariablen wann immer möglich private, methoden protected, möglichst wenig public.
Sehr sinnvoll verschiedenen Programmteile in verschiedene Namespaces zu packen.
namespace Foo {
double x;
double getX();
}
namespace Foo_1 {...};
namespace Foo_2 {...};
namespace MyFoo=Foo_2;
Wie man sieht kann man sogar einen Namespace unter einem anderen Namen erreichen und so schnell zwischen zwei Namespaces wechseln.
So kann man auf Elemente aus einem Namespace zugreifen
int y=Foo::x;
using Foo::getX;
using Foo;
Mit using kann man auch als Erbe eine Methode des Vaters, die dort protected ist, als public Methode von uns selbst anbieten
class A {
protected:
double getX();
};
class B : public A {
public:
using A::getX;
};
Foo a;
Foo b=a;
ruft den Copy Konstruktor auf:
Foo::Foo(const Foo& a);
Wenn das Objekt Zeiger auf Speicher hält den es im Destruktor wieder freigeben muss kann das zu Problem führen:
Foo a;
Foo b=a;
Foo c;
Hier wird nur für a und c der Konstruktor aufgerufen, für b dagegen der Copy Konstruktor.
Am Ende wird allerdings für alle drei der Destruktor gestartet und so unter Umständen Speicher doppelt freigegeben.
Wenn es einen passenden Konstruktor gibt kann man Objekte auch durch Zuweisungen initialisieren:
class complex {
public:
complex(double r, double i) {this->re=r; this->im=i;}
complex(double r) {this->re=r; this->im=0;}
private:
double re,im;
}
complex a=complex(2.3);
complex b=2.3;
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
A A
| |
B C
\ /
D
Jetzt gibt es 2 verschiedene Objekte A, was vermutlich nicht gewünscht war. Besser A als virtuelle Basisklasse benutzen:
class A {};
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
A
/ \
B C
\ /
D
FOO a;
BAR b=(BAR)a;
FOO * c;
BAR * d=(BAR *)c;
Angenommen man hat folgende Hierarchie
class A {};
class B {};
class C : public A {};
class D : public B {};
class E : public C, public D {};
A B
| |
C D
\ /
E
Und möchte ein Objekt vom Typ A* in eines vom Typ B* konvertieren.
Dann kann man das entweder händisch konvertieren:
A * a;
C * c=(C*) a;
E * e=(E*)c;
D * d=e;
B * b=d;
Oder mit dynamic_cast
B * b=dynamic_cast<B*>a;
Falls A nicht mindestens eine virtual Methode hat wird dieser Fehler erscheinen:
error: cannot dynamic_cast `a' (of type `class A*') to type `class B* ' (source type is not polymorphic)
static_cast ist etwas schneller, überprüft aber nicht was es macht. Vermeiden.
Foo a;
Bar b;
if (typeid(a)==typeid(FOO)) ...
if (typeid(a)==typeid(BAR)) ...
if (typeid(b)==typeid(FOO)) ...
if (typeid(b)==typeid(BAR)) ...
28-10-2006 20.48
try {
...
throw(5);
...
throw(5.0);
}
// default catch, catch all
catch(int a)
{
}
catch(float b)
{
}
catch(...)
{
}
Wenn möglich angeben welche Exceptions von einer Methode geworfen werden können:
void foo() throw();
void foo() throw(X,Y);
Im Falle eines Verstoßes dagegen wird unexpected aufgerufen.
Problem:
foo() {
...
fopen(FILE);
...
if (...) throw error1;
...
fclose(FILE);
...
}
Wenn eine Exception geworfen wird muss die Datei noch geschlossen werden. Das für alle denkbaren Fälle zu behandeln kann anstrengend werden. Schöne Lösung, eine Klasse schreiben die im Konstruktor die Datei öffnet und sie im Destruktor schließt:
class X {
public:
X() { fopen(FILE); };
~X() { fclose(FILE); };
}
Jetzt kann man obiges Beispiel wie folgt umschreiben:
foo() {
...
X x;
...
if(...) throw error1;
...
}
Jetzt wird die Datei geschlossen sobald das Objekt nicht mehr im Scope ist (also auch wenn die Exception geworfen wird).
Ein generisches Objekt dass dazu benutzt werden kann ist auto_ptr aus <memory>.
Stack<int> data;
try
{
for(;;) { data.pop(); }
}
catch(data::Empty)
{
...
}
Statt Abbruchbedingungen in der for Schleife Exception fangen. Das ist aber meistens schwieriger zu lesen, vermeiden.
//#define NDEBUG
#include <cassert>
assert(a>0 && b<0);
14-11-2006 20.01
template <class Pairtype>
class Pair
{
template class Pair<int>;
template class Pair<double>;
public:
Pair();
Pair(Pairtype left, Pairtype right);
Pairtype get_left();
void set_left(Pairtype left);
protected:
Pairtype left;
Pairtype right;
};
}
template <class Pairtype>
Pair<Pairtype>::Pair(Pairtype left, Pairtype right)
{
this->left=left;
this->right=right;
}
template <class Pairtype>
Pairtype Pair<Pairtype>::get_left()
{
return left;
}
template <class Pairtype>
void Pair<Pairtype>::set_left(Pairtype left)
{
this->left=left;
}
Pairtype<int> foo;
07-09-2006 14.27
class tg {
public:
static tg& get_instance(void) {
return tg::my_instance;
}
private:
static tg my_instance;
tg() {
}
tg(const tg&);
tg& operator=(tg&);
};
tg tg::my_instance;
int main(int argc, char **argv)
{
tg& a = tg::get_instance();
}
06-08-2005 13.09
class MyClass {
public:
...
MyClass & operator+=(const MyClass & other);
}
friend bool operator< (const MyClass& a, const MyClass& b) {
return a.foo<b.foo;
}
std::ostream& operator<< (std::ostream& os, const MyClass& a) {
os << "Foo: " << a.foo << " Bar: " << a.bar << std::endl;
return os;
}
// ++i
MyInt MyInt::operator++() {
value+=1;
return value;
}
// i++
MyInt MyInt::operator++(int) {
MyInt rescue=value;
value+=1;
return rescue;
}
26-10-2006 03.58
vector <ContainerType> a;
vector <ContainerType>::iterator iter_a;
vector <ContainerType>::const_iterator constiter_b;
vector <ContainerType> c;
vector <ContainerType>::reverse_iterator inter_r;
iter_b=c.end();
iter_a=c.begin();
(*iter_a)=c.at(0);
while (iter_a!=c.end())
{
cout << *iter_a;
iter_a++;
}
if (c.end()!=c.begin()) {
iter_a=c.end();
do {
iter_a--;
cout << *iter_a << endl;
} while (iter_a!=c.begin());
}
for (iter_r=c.rbegin(); iter_r!=c.rend(); iter_r++) {
cout << *iter_r << endl;
}
for(iter_a=c.begin(),x=0;
iter_a!=c.end() && x<100;
iter_a++, x++
) {}
Wenn man etwas in eine Datenstruktur einfügen möchte braucht man einen passenden Iterator.
b.end() ist zeigt hinter das Ende der Datenstruktur, b.begin() würde Elemente überschreiben.
Lösung: back_insert_iterator
#include <vector>
#include <list>
#include <iostream>
using namespace std;
int main() {
vector<int> a;
vector<int> b;
a.push_back(1);
a.push_back(2);
a.push_back(3);
copy(a.begin(), a.end(), back_insert_iterator<vector< int > >(b));
// 1
// 2
// 3
for(vector<int>::const_iterator i=b.begin(); i!=b.end(); i++)
cout << *i << endl;
cout << endl;
list<int> myList;
back_insert_iterator< list< int > > myListIteratorBack(myList);
front_insert_iterator< list< int > > myListIteratorFront(myList);
myListIteratorBack=4;
myListIteratorBack=5;
myListIteratorFront=6;
// 6
// 4
// 5
for(list<int>::const_iterator i=myList.begin(); i!=myList.end(); i++)
cout << *i << endl;
cout << endl;
}
Statt selbst über Datenstruktur zu iterieren:
vector<int> a;
for(vector<int>::iterator i=a.begin(); i!=a.end(); i++) foo(*i);
besser vorhanden stl Algorithmen benutzen:
sort(a.begin(), a.end());
for_each(a.begin(), a.end(), foo);
Letzteres geht aber erst mal nur mit Funktionen, nicht über Methoden. Wenn man jetzt eine Klasse hat:
class X {
public:
void draw();
}
und für jedes Element eine Methoden aufrufen will muss man die über eine Funktion aufrufen:
vector<X *> data;
void foo(X * x) {
x->draw();
}
for_each(data.begin(), data.end(), foo);
Es gibt schon eine fertige Funktion
mem_fun die das leistet:
for_each(data.begin(), data.end(), mem_fun(&X::draw));
Liste nützlicher Funktionen:
functor
class mycomp {
public:
bool operator()(double x, double y) { return fabs(x) < fabs(y); }
};
vector<double> data;
sort(data.begin(), data.end(), mycomp());
30-10-2006 21.48
Überspringt den Rest des aktuellen Schleifendurchlaufs und macht mit dem nächsten weiter.
for (int i=0; i<5; i++) {
std::cout << "\n";
std::cout << i << " ";
if (i==2) continue;
std::cout << i;
}
Ausgabe
0 0
1 1
2
3 3
4 4
Bricht die Schleife komplett ab
for (int i=0; i<5; i++) {
std::cout << "\n";
std::cout << i << " ";
if (i==2) break;
std::cout << i;
}
Ausgabe
0 0
1 1
2
Statt continue und break gibt es auch noch goto! ;-)
goto foo;
...
foo:
...
if (a<=b) max=a; else max=b;
max=(a<=b) ? a : b;
| cout | #include <iostream> |
| std::cout << std::setw(8) << foo << std::endl; | #include <iomanip> |
| fabs(int) | #include <cstdlib> |
| fabs(float) | #include <cmath> |
29-10-2006 02.17
Gegeben folgendes kleines Projekt:
new.cpp
#include "old.h"
class newClass
{
public:
int newClassMethod(int x) {
return proxy(x);
}
};
int main(int argc, char **argv) {
newClass newObject;
return newObject.newClassMethod(5);
}
old.h
int proxy(int x);
old.cpp
#include "old.h"
class oldClass
{
public:
int myold(int x) {
return 25;
}
};
int proxy(int x) {
oldClass oldObject;
return oldObject.myold(x);
}
In new.cpp wird ein Objekt der Klasse newClass angelegt welche Code aus der Datei old.cpp aufruft. Dieses Projekt sollte ohne Probleme compilieren:
g++-2.95 -c old.cpp -o old.o
g++-2.95 -c new.cpp -o new.o
g++-2.95 old.o new.o -o myproject
Spannend wird es wenn old.cpp mit g++-2.95 compiliert wurde und man jetzt gerne für alle anderen Dateien außer old.cpp (z.B. weil man keinen Zugang zu den Quellen von old.o um es neu zu kompilieren) einen neueren Compiler nutzen möchte
g++-2.95 -c old.cpp -o old.o
g++-3.3 -c new.cpp -o new.o
g++-3.3 old.o new.o -o myproject
Das wird fehlschlagen
new.o(.gnu.linkonce.t._ZN8newClass14newClassMethodEi+0xd): In function `newClass::newClassMethod(int)':
: undefined reference to `proxy(int)'
collect2: ld returned 1 exit status
weil sich die
ABI zwischen gcc-3.2 und seinen Vorgängerversionen unterscheidet. Mit einem kleinen Trick klappt es aber doch. Da sich die ABI nur für C++ Code geändert hat genügt es einfach eine Funktion als C Funktion zu deklarieren und sie ebenfalls mit dem alten Compiler compilieren. Dann kann sie als Schnittstelle zwischen beiden Welten dienen. In diesem Beispiel muss man nur diese kleine Änderung vornehmen:
old.h
extern "C"
{
int proxy(int x);
}
Und schon kann man sie als Schnittstellen zwischen beiden Welten nutzen.
Probleme gibt es aber wenn sowohl neue als auch alte Dateien z.B. die stl benutzen wollen. Aber auch dafür scheint es eine Lösung zu geben:
Beispielsprojekt:
newClass.cpp,
newClass.h,
new.cpp ,
oldClass.cpp ,
oldClass.h,
old.cpp,
old.h.
Als erstes alle Dateien mit dem jeweiligen Compiler compilieren
g++-2.95 -c old.cpp -o old.o
g++-2.95 -c oldClass.cpp -o oldClass.o
g++-3.3 -c new.cpp -o new.o
g++-3.3 -c newClass.cpp -o newClass.o
Jetzt alle alten .o Dateien statisch zu einer neuen .o zusammenfassen. Wie das geht kann man dem Befehl:
g++-2.95 -v -static -r -o allOldStuff.o old.o oldClass.o
entnehmen. Allerdings würden dabei im nächsten Schritt einige .o Dateien doppelt eingelesen. Lässt man diese weg sieht es ungefähr so aus:
/usr/lib/gcc-lib/i386-linux/2.95.4/collect2 -m elf_i386 -static -o allOldStuff.o -r -L/usr/lib/gcc-lib/i386-linux/2.95.4 old.o oldClass.o -lstdc++ -lm -lgcc -lc -lgcc /usr/lib/gcc-lib/i386-linux/2.95.4/crtend.o /usr/lib/crtn.o
Jetzt kann man alle neuen .o Dateien gegen die zusammengefasste alte .o Datei linken:
g++ -o MyProgram new.o newClass.o allOldStuff.o
Fertig. :-)
Leider scheint es in diesem Fall zur Laufzeit Probleme zu geben.
06-09-2006 00.01
Danke an
Richard Eckart für die vielen Hinweise!
18-05-2008 15.06