Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6500
Wohnort: Hamburg
|
Ich benötige mal wieder Hilfe in Sachen C++. In einer Funktion benötige ich eine Bewertungstabelle. In C ist das ja kein Problem, da packt man einfach irgendwo ein globales Array hin und fertig. In C++ ist sowas eher unüblich. Ich habe jetzt sowas versucht: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | class BitFilter {
private:
static const char filter_tab[128] = {
/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0. */
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, /* 1. */
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, /* 2. */
0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, /* 3. */
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, /* 4. */
0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, /* 5. */
0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, /* 6. */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* 7. */
};
char * bitfilter;
int mask; // filter mask
public:
BitFilter( int n );
~BitFilter();
void setup( int n );
int filter( int b );
};
...
inline int
BitFilter::filter( int b ) {
return bitfilter[ b & mask ];
}
|
was der Compiler mit
md01.cpp:210: Fehler: ISO-C++ verbietet Deklaration von »filter_tab« ohne Typ
md01.cpp:210: Fehler: ungültige Initialisierung innerhalb der Klasse des statischen Datenelements vom nicht eingebauten Typen »const int [128]«
quittiert. Wie komme ich aus der Nummer wieder raus? Ist ein globales Array wirklich die einzige Lösung?
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6500
Wohnort: Hamburg
|
Ich bin nicht sicher ob ich das jetzt richtig verstanden habe, aber irgendwie klingt das so ähnlich wie das, was eine bekannte Suchmaschine auch gefunden hat, und was ich doof finde. Mein Ziel war eigentlich, das der Anwender dieses Filters (bzw. Bewertungsfunktion) keine Berührung mit dem Inhalt der Bewertungstabelle hat. Außerdem wollte ich verhindern, das der Compiler für jede neue Instanz eine neue Bewertungstabelle anlegt (die später möglicherweise auch noch größer werden kann). Deshalb wollte ich die Tabelle auch in der Headerdatei haben. Wenn das so nicht geht, ist es möglicherweise besser das Ganze doch wieder als externes C Modul zu verwirklichen und dann mittels
extern "C" { ... }
einzubinden. Da muss der Anwender zumindest den Tabellenaufbau nicht kennen. Das "inline" Feature kann ich dann aber wohl vergessen. Einziger Trost ist, das der Verwaltungsoverhead für C Funktionen geringer ist als für C++ Funktionen. Aber vielleicht hat ja jemand noch eine andere Idee. Da es irgendwann mal eine Echtzeitanwendung werden soll, geht es mir dabei vorwiegend um kurze Ausführungszeiten. Speicherplatz ist Nebensache.
|
Panke
Anmeldungsdatum: 14. Oktober 2010
Beiträge: 133
|
Dakuan schrieb: Ich bin nicht sicher ob ich das jetzt richtig verstanden habe, aber irgendwie klingt das so ähnlich wie das, was eine bekannte Suchmaschine auch gefunden hat, und was ich doof finde.
In C++2003 kannst du statische Klassenattribute nur so wie beschrieben initialisieren.
Mein Ziel war eigentlich, das der Anwender dieses Filters (bzw. Bewertungsfunktion) keine Berührung mit dem Inhalt der Bewertungstabelle hat.
Hat er auch nicht, der Inhalt von *cpp-Dateien gilt gemeinhin als Implementierungsdetail.
Außerdem wollte ich verhindern, das der Compiler für jede neue Instanz eine neue Bewertungstabelle anlegt (die später möglicherweise auch noch größer werden kann). Deshalb wollte ich die Tabelle auch in der Headerdatei haben.
Genau das erhälst du, indem du sie als static in der Klasse deklarierst und in einer Übersetzungseinheit definierst.
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6500
Wohnort: Hamburg
|
So, heute morgen sah die Sache viel klarer aus, dachte ich zumindest. Wegen einiger irreführender Fehlermeldungen habe ich mich noch ein wenig im Kreis gedreht. Da wurde dann sogar der Konstruktor angemeckert, weil das Array nicht initialisiert wurde oder es kommt sowas:
md01.cpp:210: Fehler: Deklaration von »char BitFilter::filter_tab [128]« außerhalb einer Klasse ist keine Definition /* static */ const char BitFilter::filterTab[128] = { ... }; // keine Ahnung, ob static hier nötig.
Das hat er mir ebenfalls um die Ohren gehauen:
md01.cpp:210: Fehler: »static« darf nicht bei der Definition (im Gegensatz zu Deklaration) eines statischen Datenelementes verwendet werden Aber jetzt übersetzt cpp zumindest ohne irgendetwas anzunörgeln. Ob es auch funktioniert muss ich noch ausprobieren.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13205
|
Es ist, wie Panke schon gesagt hat: Deklaration in den Header, Definition in die C++-Quelle. Das ist auch ganz logisch, weil alle Benutzer der Klasse das Member kennen können müssen (könnte ja auch "public" sein). Die Definition mit Initialisierung liegt aber nur in einer Objekt-Datei. | $ g++ -c -Wall -ansi x.cxx
|
(Keine Fehler.) Die Quelle sieht so aus (ich habe hier der Einfachheit halber den Header weggelassen: |
// header
class X {
static const char foo[];
};
// source
const char X::foo[] = { 'a', 'b' };
|
Alternativer Ansatz: Du erwähnst die Konstante gar nicht im Header und deklarierst sie in der Datei als "static": Datei y.hxx
|
// header
class X {
// nix
public:
void test();
};
|
Datei y.cxx
|
#include "y.hxx"
// source
#include <iostream>
static const char foo[] = { 'a', 'b' };
void X::test() {
std::cout << foo[0] << std::endl;
}
|
Datei yy.cxx:
|
#include "y.hxx"
int main() {
X x;
x.test();
return 0;
}
|
Testlauf:
| $ rm *.o
$ g++ -c -Wall -ansi y.cxx yy.cxx
$ ls -l *.o
-rw-r--r-- 1 rklemme None 2.4K Oct 14 09:35 y.o
-rw-r--r-- 1 rklemme None 887 Oct 14 09:35 yy.o
$ g++ -Wall -ansi -o run *.o
$ ./run
a
|
Ciao robert
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6500
Wohnort: Hamburg
|
Ja, so wie ihr beide es vorgeschlagen habt, funktionierte es gestern Abend dann auch (nachdem ich auch die der eigentliche Funktion nachgebessert hatte). Ich wollte mich auch gestern schon melden, aber ich war damit beschäftigt einen nervigen "segmentation fault' zu jagen. Da war ich wiedermal ein Opfer von Murphy geworden. (Wenn man einen Fehler durch ändern einer Variablen in 3 von 4 Modulen beseitigen kann, liegt der Fehler im 4. Modul.)
Alternativer Ansatz: Du erwähnst die Konstante gar nicht im Header und deklarierst sie in der Datei als "static":
Das wollte ich eigentlich zuerst auch. Hab's dann aber gelassen, weil globale Daten meist als schlechter Stil abgetan werden. Ich vergaß dabei dass "static" ja auch den Sichtbarkeitsbereich begrenzt. Ich tendiere jetzt aber doch dazu den "alternativen Ansatz" zu verwirklichen. Danke für die Hilfe.
|
tischbein
Anmeldungsdatum: 21. Juli 2008
Beiträge: 404
|
Du könntest auch std::list verwenden, welches sich genau wie ein statisches C-style Array verhält; d.h., die werte sind statisch, und werden auch statisch im Speicher gehalten, genau wie C Arrays. Der unterschied ist aber, dass std::list eine angenehmere syntax hat, und sich zudem leichter iterieren lässt. Ist natürlich dir überlassen. Hier wäre z.B. ein beispiel: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
#include <list>
class Blah
{
private:
/* mit initializer_list, welche dann mit for(auto& elem: stuff)
verwendet werden kann ... */
std::list<int> stuff =
{
10, 20, 30, 40, 50, 60, 70, 80, 90
};
public:
Blah()
{
/* C++11-style iteration */
for(auto& elem: stuff)
{
/* irgendwas mit elem ... */
}
}
};
|
Und natürlich als C++11 kompilieren:
g++ -std=c++11 blah.cpp Da kann man statisch-verwendete elemente auch gleich im Körper der klasse definieren, ohne diese dann noch extra ausserhalb des klassenkörpers definieren zu müssen, wie das ja bei C++98 der fall war. Aber wie gesagt, ist dier überlassen. Wie gesagt, es würde sich genau wie statische C Arrays verhalten.
|
Lysander
Anmeldungsdatum: 30. Juli 2008
Beiträge: 2669
Wohnort: Hamburg
|
tischbein schrieb: Du könntest auch std::list verwenden, welches sich genau wie ein statisches C-style Array verhält; d.h., die werte sind statisch, und werden auch statisch im Speicher gehalten, genau wie C Arrays. Der unterschied ist aber, dass std::list eine angenehmere syntax hat, und sich zudem leichter iterieren lässt. Ist natürlich dir überlassen.
Viel wichtiger ist eigentlich, dass die Zeitkomplexität beim Zugriff sich eben nicht wie bei Arrays verhält! Da das Ding sich eben nicht ändert, bleibt *das* als tatsächlicher Knackpunkt. Und da ist eine doppelt verkettete Liste bei einem Index-Zugriff denkbar schlecht geeignet. Wenn schon, sollte er std::vector nehmen - ich tendiere auch zu solchen Containern, jedoch mag es hier keine großen Vorteile dafür geben. Wichtig ist dabei, dass man das algorithmische Laufzeitverhalten der Container stets im Blick hat und abhängig vom Anwendungsfall den optimalen wählt. Stumpf einen "Lieblings"-Container nutzen führt zu schlechtem Design...
|
Dakuan
(Themenstarter)
Anmeldungsdatum: 2. November 2004
Beiträge: 6500
Wohnort: Hamburg
|
Danke nochmal für die Tipps, aber ich bin in C++ noch nicht so weit fortgeschritten, das ich das jetzt sofort umsetzen könnte. Ich denke eigentlich noch in C.
Wichtig ist dabei, dass man das algorithmische Laufzeitverhalten der Container stets im Blick hat und abhängig vom Anwendungsfall den optimalen wählt.
In der Tat hat hier die Ausführungszeit oberste Priorität, sonst hätte ich nicht die Tabellenlösung gewählt (die ist inzwischen schon auf 512 Einträge angewachsen).
|