Thread View: de.comp.lang.c++
3 messages
3 total messages
Started by Andreas Zierdt
Thu, 18 Apr 1996 16:05
vector, einige Fragen
Author: Andreas Zierdt
Date: Thu, 18 Apr 1996 16:05
Date: Thu, 18 Apr 1996 16:05
58 lines
3342 bytes
3342 bytes
Für meine Arbeit an der Uni habe ich u.a. eine Klasse "Vector"
entwickelt; ich bin dabei auf verschiedene Probleme gestossen, die ich
hier in Form von drei Fragen zusammenfasse:
1. Ein Vektor im mathematischen Sinne ist ein Element einer Menge (des
Vektorraums), für die bestimmte Axiome erfüllt sind. In der Physik sind
das z.B. gerichtete Größen wie Kräfte, aber auch abstrakte Gebilde wie
Zustandsvektoren. Ich bin mir nun nicht sicher inwieweit der Begriff
"vector" im Sinne von C++ dieser Definition entspricht. Mein Eindruck ist
das hier vector im Sinne von "objectorientiertes array" verwendet wird.
Falls das stimmt, halte ich es für eine unglückliche Namensgebung. Die
Frage lautet dann: wie sollte eine Klasse von "richtigen" Vektoren heißen?
2. Für meine Vector-Klasse habe ich folgende Konstruktoren definiert:
Vector(unsigned int dimension=3);
Vector(const Vector& v);
Vector(const Matrix<T>& Ma);
Nun stellt sich die Frage, wie kann ich konstante Vektoren erzeugen?
"Vector(unsigned int dimension=3)" erlaubt mir keine Startwerte vorzugeben
und eine spätere Zuweisung an const-Objekte ist ja nicht möglich. Da die
Dimension des Vektors unbekannt ist, käme nur etwas in der Form
"Vector(unsigned int dimension=3, double ...)" in Frage. Die Verwendung der
Ellipse ist aber nicht nur unschön, sie Verhindert auch die Verwendung einer
Initialisiererliste im Konstruktor. (oder?)
Eine Initialisiererliste ist aber erforderlich, wenn die Datenelemente als
const deklariert werden, oder ist ein const Objekt mit nicht const Daten
möglich/sinnvoll?
3. Für die Fehlerbehandlung verwende ich eine eingebettete Klasse:
struct Vector_Error
{
Vector_Error(int n, const Vector& orig) : Nr(n), ErrOrigin(orig) {}
int Nr;
const Vector& ErrOrigin;
};
Dabei werden in "Nr" Fehlercodes abgelegt (Range, ect.) die im Fehlerhandler
wieder dekodiert werden müssen, was einen gewissen overhead erzeugt. Eine
Alternative wäre eine eigene Klasse für jede Fehlerursache, nur kann ich dann
Vektor-Fehler nicht mehr mit einem catch() abfangen, sondern muß 5 oder 10
verwenden. Oder ich gebe die Einbettung auf und verwende hierachische Fehler-
klassen, dann geht aber die Datenkapselung verloren.
Hier interessiert mich einfach mal, wie ihr es macht.
Gruß
az
P.S. Die OS-spezifischen und off-topic Beiträge haben wirklich unerträglich
zugenommen, IMHO hilft da aber kein rumdirigieren, sondern es müssen wieder
mehr C++ Themen gepostet werden. Im Augenblick könnte man ja glauben, dies
ist eine Windows-Gruppe und dann kommen auch solche Beiträge.
---
Andreas Zierdt, Institut fuer Phonetik und sprachliche Kommunikation
Universitaet Muenchen, Schellingstr. 3
80799 Muenchen, Germany
E-mail: andi@sun1.phonetik.uni-muenchen.de
Re: vector, einige Fragen
Author: Ralf W. Stephan
Date: Fri, 19 Apr 1996 10:53
Date: Fri, 19 Apr 1996 10:53
53 lines
2600 bytes
2600 bytes
Andreas Zierdt writes:
> ..."vector" im Sinne von C++ dieser Definition entspricht. Mein Eindruck ist
> das hier vector im Sinne von "objectorientiertes array" verwendet wird.
> Falls das stimmt, halte ich es für eine unglückliche Namensgebung. Die
> Frage lautet dann: wie sollte eine Klasse von "richtigen" Vektoren heißen?
Du gehst noch davon aus, daß es nur einmal den Namen 'vector' geben
kann. Das ist aber durch 'namespace' viel besser geworden. Siehe
dazu eins der neueren C++ Bücher oder den Draft-Standard.
> 2. Für meine Vector-Klasse habe ich folgende Konstruktoren definiert:
> Vector(unsigned int dimension=3);
> Vector(const Vector& v);
> Vector(const Matrix<T>& Ma);
> Nun stellt sich die Frage, wie kann ich konstante Vektoren erzeugen?
Es ist etwas unklar, *warum* Du das haben willst. Aber okay:
a) Vector temp(...)
temp.init();
const Vector EinheitsVektor = temp;
b) Oder Du leitest Klassen für spezielle Vektoren ab, das ist eh besser,
denn Du scheinst ja Vektoren für unterschiedliche Zwecke zu haben:
class EinheitsVektor : public Vector
{
EinheitsVektor (int dimension=...);
...
};
const EinheitsVektor ev;
> "Vector(unsigned int dimension=3, double ...)" in Frage. Die Verwendung der
> Ellipse ist aber nicht nur unschön, sie Verhindert auch die Verwendung einer
> Initialisiererliste im Konstruktor. (oder?)
Keine Ahnung, aber ich mag die Punkte auch nicht... 8)
> Eine Initialisiererliste ist aber erforderlich, wenn die Datenelemente als
> const deklariert werden, oder ist ein const Objekt mit nicht const Daten
> möglich/sinnvoll?
Warum machst Du denn die Elemente nicht privat? Ein const Objekt
hat IMHO sowieso nur const Daten, anders geht es nicht.
> Alternative wäre eine eigene Klasse für jede Fehlerursache, nur kann ich dann
> Vektor-Fehler nicht mehr mit einem catch() abfangen, sondern muß 5 oder 10
> verwenden. Oder ich gebe die Einbettung auf und verwende hierachische Fehler-
> klassen, dann geht aber die Datenkapselung verloren.
Also, ich würde auf jeden Fall eine Hierarchie verwenden, d.h. jede
Exception von einer Basisexception-Klasse ableiten (s. <std/stdexcept.h>)
ralf
Re: vector, einige Fragen
Author: Dietmar Kuehl
Date: Sun, 21 Apr 1996 17:30
Date: Sun, 21 Apr 1996 17:30
263 lines
10012 bytes
10012 bytes
Hi,
Andreas Zierdt (andi@linux8.phonetik.uni-muenchen.de) wrote:
: 1. Ein Vektor im mathematischen Sinne ist ein Element einer Menge (des
: Vektorraums), für die bestimmte Axiome erfüllt sind. In der Physik sind
: das z.B. gerichtete Größen wie Kräfte, aber auch abstrakte Gebilde wie
: Zustandsvektoren. Ich bin mir nun nicht sicher inwieweit der Begriff
: "vector" im Sinne von C++ dieser Definition entspricht. Mein Eindruck ist
: das hier vector im Sinne von "objectorientiertes array" verwendet wird.
Der C++ 'vector' ist in der Tat schwerlich ein Vektor im mathematischen
Sinne: Er kann fuer Klassen instantiiert werden, die sicher nicht den
Bedingungen eines Koerpers genuegen (wenn ich mich richtig entsinne,
werden Vektorraeume ueber Koerpern gebildet; die Koerperaxiome muessen
aber sicher nicht erfuellt sein, um einen 'vector' zu instantiieren).
Andererseits ist die Bezeichnung 'vector' durchaus konform zur
Verwendung in weiten Bereichen der Informatik. Insofern kann man schwer
gegen diese Namensgebung wettern...
: Falls das stimmt, halte ich es für eine unglückliche Namensgebung. Die
: Frage lautet dann: wie sollte eine Klasse von "richtigen" Vektoren heißen?
'valarray'. Das ist zumindest der Name, den eine solche Klasse in der
Standard C++ Bibliothek hat. Allerdings bin ich mir nicht ganz sicher,
ob man nicht auch ein 'valarray' fuer eine Klasse instantiieren kann,
die nicht den Koerperaxiomen genuegt.
Ansonsten, wenn man sich gerade nicht im Namespace 'std' aufhaellt,
haelt ein nichts davon ab, nicht auch eine Klasse 'vector' zu
definieren: Halt in einem anderen Namespace als 'std' (in 'std' darf
man eh nichts definieren, obwohl es nichts gibt was einen davon
abhaelt...). Etwas Vorsicht ist allerdings schon geboten: Man koennte
leicht irritiert werden, wenn 'vector' auf einmal etwas anderes meint,
als 'std::vector'.
: 2. Für meine Vector-Klasse habe ich folgende Konstruktoren definiert:
: Vector(unsigned int dimension=3);
: Vector(const Vector& v);
: Vector(const Matrix<T>& Ma);
: Nun stellt sich die Frage, wie kann ich konstante Vektoren erzeugen?
Mit diesen drei Konstruktoren fuer Vektoren sieht es fuer konstante
Vektoren nicht besonders gut aus. Was man machen koennte, waere etwas
der Art:
template <unsigned int dim> struct int_dummy {}
template <unsigned int dim>
Vector const &NullVektor(int_dummy<dim> const &)
{
static bool flag = true;
static Vector null_vektor(dim);
if (flag)
{
flag = false;
// ... do some initialization
}
return null_vektor;
}
Diese Funktion ist aufzurufen als
NullVektor(int_dummy<4>());
Falls das keine Alternative ist, kann man einen weiteren Konstruktor
hinzufuegen:
template <class T>
Vektor<T>::Vektor(unsigned int dim, T const *init):
// member-initializer-list
{
for (unsigned int i = 0; i < dim; ++i)
(*this)[i] = init[i];
}
int null_3[] = {0, 0, 0};
const Vektor<int> NullVektor(3, null_3);
: "Vector(unsigned int dimension=3)" erlaubt mir keine Startwerte vorzugeben
: und eine spätere Zuweisung an const-Objekte ist ja nicht möglich. Da die
: Dimension des Vektors unbekannt ist, käme nur etwas in der Form
: "Vector(unsigned int dimension=3, double ...)" in Frage. Die Verwendung der
Wie sieht es mit folgenden, vielleicht etwas Ungewohntem, aus:
#include <iostream.h>
#include <vector>
#include <algorithm>
#include <iterator>
template <class T>
class vec_init
{
private:
vector<T> init;
// disallow the use of copy ctor and assignment
vec_init(vec_init const &);
void operator= (vec_init const &);
public:
typedef vector<T>::const_iterator iterator;
vec_init() {} // nothing to do...
vec_init &operator << (T const &val)
{
init.push_back(val);
return *this;
}
unsigned int size() const { return init.size(); }
iterator begin() const { return init.begin(); }
iterator end() const { return init.end(); }
};
template <class T>
class vec
{
private:
unsigned int size;
T *values;
public:
vec(vec_init<T> const &vi):
size(vi.size()),
values(new T[size])
{
copy(vi.begin(), vi.end(), values);
}
T const *begin() const { return values; }
T const *end() const { return values + size; }
};
vec<int> const v(vec_init<int>() << 1 << 2 << 3);
int main()
{
copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
return 0;
}
Keine Ellipse und ueberhaupt ist alles wunderschoen sauber: Man kann
die groesse nicht verpatzen und trotzdem ist das Objekt 'const':
einfach gut, nicht wahr?
: Ellipse ist aber nicht nur unschön, sie Verhindert auch die Verwendung einer
: Initialisiererliste im Konstruktor. (oder?)
Keine Ahnung. Sollte mich wundern, wenn man ein Vararg-Interface fuer
Klassen verwenden kann, dass man dann keine Member-Initializer-Liste
verwenden kann. Allerdings habe ich mich darum noch nie gekuemmert,
weil, IMHO, die Ellipse nur zu Kompatibilitaet mit C existiert und es
in C++ keinen Grund gibt, sowas zu verwenden.
: Eine Initialisiererliste ist aber erforderlich, wenn die Datenelemente als
: const deklariert werden, oder ist ein const Objekt mit nicht const Daten
: möglich/sinnvoll?
Bahnhof? Ein Objekt 'obj' der Klasse 'T', das als 'T const obj'
deklariert ist, ist 'const'. Ob die Member von 'T' auch 'const'
deklariert werden ist dabei vollkommen irrelevant: Alle Member von
'obj', die nicht explizit 'mutable' deklariert worden sind, sind
konstant und das Ergebnis eines Versuchs einen dieser Member zu aendern
(etwas mit 'const_cast<>') ist undefiniert.
: 3. Für die Fehlerbehandlung verwende ich eine eingebettete Klasse:
: struct Vector_Error
: {
: Vector_Error(int n, const Vector& orig) : Nr(n), ErrOrigin(orig) {}
: int Nr;
: const Vector& ErrOrigin;
: };
Das sieht uebrigens nach ziemlich gefaehrlichem Code aus! Ich stelle mir da
sowas vor:
template <class T>
T &Vector<T>::operator[] (int idx)
{
if (idx > dim)
throw(Vector_Error(OUT_OF_RANGE, *this);
return representation[idx];
}
... und dazu ein Aufruf wie z.B.
void f()
{
Vector<int> vec(3);
cout << vec[3] << endl; // oops, throws an exception
}
Dummerweise wird in der Exception ein 'Vektor' gespeichert, der zu
allem Ueberfluss auch noch zerstoert wird ('f()' faengt keine
Exception, die das verhindern wuerde) und schon landen wir ganz schnell
bei undefiniertem Verhalten! Den Vektor, fuer den der Fehler
aufgetreten ist, kann man nur dann speichern, wenn man sicher weiss,
dass er nicht zerstoert wird. Ich vermute, dass das nie der Fall ist
(man kann es sicherstellen, indem man eine Indirektion einfuehrt: Man
merkt sich in der Exception ein Objekt, dass die eigentliche
Representation referenziert; mittels Reference-Counting wird
sichergestellt, dass diese nicht geloescht wird).
: Dabei werden in "Nr" Fehlercodes abgelegt (Range, ect.) die im Fehlerhandler
: wieder dekodiert werden müssen, was einen gewissen overhead erzeugt. Eine
: Alternative wäre eine eigene Klasse für jede Fehlerursache, nur kann ich dann
: Vektor-Fehler nicht mehr mit einem catch() abfangen, sondern muß 5 oder 10
: verwenden. Oder ich gebe die Einbettung auf und verwende hierachische Fehler-
: klassen, dann geht aber die Datenkapselung verloren.
: Hier interessiert mich einfach mal, wie ihr es macht.
In erster Linie mit einer anderen Analyse der Moeglichkeiten:
1. es gibt meist keinen guten Grund, nicht von einer der Standard
Exception-Klassen abzuleiten. Damit kann man ALLE Exceptions mit
EINER einzigen 'catch()' Klause abfangen:
int main()
{
try
{
// hier wird die eigentliche Arbeit gemacht
}
catch (std::exception const &ex)
{
// egal was geworfen wird, hier kommt es nicht dran vorbei
}
catch (...)
{
// ... es sei denn, irgendwer haelt sich nicht an die
// Konvention, von den Standard-Exceptions abzuleiten:
// dann landen wir hier...
}
2. Nichts spricht gegen eine Hierarchie INNERHALB einer anderen Klasse:
class foo
{
public:
class bar {};
class foobar: public bar {};
class barfoo: public foobar {};
// aber auch:
class foo_exception: public std::logic_error { /*...*/ };
};
3. Wenn man spezifische Exceptions fangen moecht, waehrend man andere
allgemein Behandeln moechte, so ist das NUR durch verschiedene
Klassen moeglich, jedoch nicht mit Error-Codes.
Ergo: Eine Klassenhierarchie ist um Laengen besser als Fehlercodes und
es gibt keinen echten Grund, keine Klassenhierachie zu verwenden.
Darueber hinaus sollte man von den Standard Exception Klassen
ableiten: Damit kann man alle Exceptions bequem mittels
'std::exception' fangen bzw. bestimmte Sub-Klassen von
'std::exception' unterschiedlich behandeln. Ausserdem sieht man der
Klasse direkt an, welchen Typ von Fehlern sie transportiert (da man ja
bekanntlich mit der Standard-Library sehr vertraut ist und saemtliche
Details jederzeit weiss :-).
--
dietmar.kuehl@uni-konstanz.de
http://www.informatik.uni-konstanz.de/~kuehl/
I am a realistic optimist - that's why I appear to be slightly pessimistic
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads