Forum: PC-Programmierung C++: Array mit Konstanten?


von Michael N. (much)


Lesenswert?

Hallo,

Ich versuche gerade meine C++ Kenntnisse aufzufrischen. Dabei bin ich 
auf folgendes Szenario gestoßen.

Ich habe eine Klasse A, von der im Hauptprogram zwischen 1 und 10 
Instanzen erzeugt werden. Klasse A enthält eine Memberfunktion, die auf 
Daten aus einer Tabelle zugreifen muss. Die Tabelle hat eine Größe von 
10 x 5 in der konstannte Parameter eingespeichert sind. (Die n-te 
Instanz von Klasse A soll die Parameter aus der n-ten Zeile der Tabelle 
entnehmen.)

Nun stellt sich mir die Frage wie, bzw. wo ich die Tabelle am besten 
deklariere und initialisiere! Meine bisherige Lösung sieht in etwa wie 
folgt aus. Meine Frage ist allerdings ob man das Problem irgendwie 
besser lösen kann.
1
static const double param[10][5] = {{1.0000, ... 0.0000},
2
                                    ...
3
                                    {1.0215, ... 0.2883}};
4
5
class A
6
{
7
public:
8
  A(void);
9
  double foo(unsigned int n);
10
};
11
12
double A::foo(unsigned int n)
13
{
14
  double x;
15
  double v[5];
16
  // v =  n-te Zeile von param
17
  // Berechne irgendetwas mit v.
18
  return x;
19
}

von Peter II (Gast)


Lesenswert?

Ich würde der Klasse im Konstruktor die n-Zeile mitgeben.

von Di P. (drpepper) Benutzerseite


Lesenswert?

Wenn die Tabelle wirklich konstant ist, hast du schon einen geeigneten 
Ort gefunden, um sie abzulegen. Es macht ggf. Sinn, sie in eine eigene 
Headerdatei auszulagern und ihr einen Namespace zu geben.

Wenn sich die Werte nie ändern sollen, würde ich Peter II's Vorschlag 
wählen, und die Zeilennummer im Konstruktor übergeben und in einer 
Instanzvariable speichern.
1
A::A(unsigned int n) {
2
  myN = n;
3
}
4
5
void A::zugriffAufParam() {
6
  for(int i = 0; i < 5; ++i) {
7
    tuWasMit(param[myN][i]);
8
  }
9
}

Sollen sich die Werte nach der Zuordnung ändern können (z.B. für 
adaptive Filter) müssen sie eben in einen eigenen std::vector oder ein 
Array übertragen werden.

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Hallo Michael,

Michael N. schrieb:
> Nun stellt sich mir die Frage wie, bzw. wo ich die Tabelle am besten
> deklariere und initialisiere!

wenn Du sie nur in einer Funktion benötigst, dann würde ich sie auch 
genau nur dort deklarieren. Das hat den Vorteil, dass Du einen knappen 
Namen wählen kannst und dass dieser Name dann auch ausserhalb der 
Funktion nicht sichtbar ist.
1
double A::foo(unsigned int n) const
2
{
3
  static const double param[10][5] = {{1.0000, ... 0.0000},
4
                                      ...
5
                                      {1.0215, ... 0.2883}};
6
  double x = ...;
7
  auto& const v = param[ n ];
8
  // v =  n-te Zeile von param
9
  // Berechne irgendetwas mit v.
10
  return x;
11
}

mfg Torsten

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

Di P. schrieb:
> und die Zeilennummer im Konstruktor übergeben und in einer
> Instanzvariable speichern.

Ich würde etwas weiter gehen und eine const-Referenz auf die Zeile 
übergeben und als Instanzvariable speichern. Das würde die Abhängigkeit 
von A von der Tabelle deutlicher beschreiben als eine Zahl, die sich auf 
irgendwelche globalen Daten, die irgendwie auch irgendwo vorhanden sein 
müssen, bezieht.  Da die Referenz nur auf existierende Zeilen zeigen 
kann¹, würde sich auch das Problem der Bereichsüberprüfung für die 
Zeilennummer im Konstruktor lösen. Die wird bei näherer Betrachtung 
nämlich hässlich: Lässt man n=23 durchgehen und überlässt dem Aufrufer 
die Verantwortung? Exception aus dem Konstruktor? Still und leise auf 
legale Werte beschränken? Aufpassen und beten?



¹solange man sich nicht besondere Mühe gibt.

von Michael N. (much)


Lesenswert?

Tom schrieb:
> Ich würde etwas weiter gehen und eine const-Referenz auf die Zeile
> übergeben und als Instanzvariable speichern. Das würde die Abhängigkeit
> von A von der Tabelle deutlicher beschreiben als eine Zahl, die sich auf
> irgendwelche globalen Daten, die irgendwie auch irgendwo vorhanden sein
> müssen, bezieht.

Irgendwie blicke ich nicht ganz durch wie ich die Zeile als 
const-referenz übergeben kann. Wie sieht denn die entsprechende 
deklaration aus?

Ich habs mit
1
A(const double (&ab[5]));
versucht, aber der Compiler meldet sich dann mit der Meldung:
1
error: declaration of ‘ab’ as array of references

von Mikro 7. (mikro77)


Lesenswert?

const double array reference mit 5 werten:
1
f(const double (&array)[5]);

von Tom (Gast)


Angehängte Dateien:

Lesenswert?

Man könnte auch das, was bisher eine Arrayzeile ist, als eigenen 
Datentyp, nämlich einen Satz an Konfigurationsdaten, ansehen und das so 
hinschreiben.

von Michael N. (much)


Lesenswert?

S. J. schrieb:
> const double array reference mit 5 werten:
> f(const double (&array)[5]);

Danke, so funktioniert es! Allerdings wird das von Tom beschrieben 
Problem:
Tom schrieb:
> Da die Referenz nur auf existierende Zeilen zeigen
> kann¹, würde sich auch das Problem der Bereichsüberprüfung für die
> Zeilennummer im Konstruktor lösen.
dadurch auch nicht wirklich gelöst.
Die Zeile
1
n = 12;
2
Stage(coeff::ai[n], coeff::bi[n])
kann im Programm so ausgefürht werden und liefert dann halt irgedwelchen 
Käse, da ai[12] und bi[12] eigentlich schon außerhalb des 
Speicherbereichs liegen.

von Mikro 7. (mikro77)


Lesenswert?

Ich sehe bei der Aufgabenstellung nur einen Parameter: Ein integer 
zwischen 0 und 10. Ausgabewert ist ein double. Dafür muss man kein 
Klassengerüst bauen. Alles was es an public Funktionen braucht ist bspw:
1
double magic(unsigned);

Die (pseudo) Implementierung könnte dann folgendermaßen aussehen...
1
static double makeMagic(double x1, ... x5)
2
{
3
   ...do and return your magic stuff...
4
}
5
6
double magic(unsigned i)
7
{
8
  static std::array<double,10> const array = {{
9
     makeMagic(1.0000, ... 0.0000),
10
     ...
11
     makeMagic(1.0215, ... 0.2883) }} ;
12
  return array.at(i) ;
13
}

von Tom (Gast)


Lesenswert?

Michael N. schrieb:
> dadurch auch nicht wirklich gelöst.

Du hast völlig recht, der Koffeinspiegel war zu weit abgesunken und ich 
habe Mist geschrieben. Das Gefummel mit std::get oben in hip.cpp würde 
das lösen, aber die Template-Hölle des modernen C++ ist nicht jedermans 
Sache...

von Sheeva P. (sheevaplug)


Lesenswert?

Michael N. schrieb:
> Ich versuche gerade meine C++ Kenntnisse aufzufrischen. Dabei bin ich
> auf folgendes Szenario gestoßen.
>
> Ich habe eine Klasse A, von der im Hauptprogram zwischen 1 und 10
> Instanzen erzeugt werden. Klasse A enthält eine Memberfunktion, die auf
> Daten aus einer Tabelle zugreifen muss. Die Tabelle hat eine Größe von
> 10 x 5 in der konstannte Parameter eingespeichert sind. (Die n-te
> Instanz von Klasse A soll die Parameter aus der n-ten Zeile der Tabelle
> entnehmen.)
>
> Nun stellt sich mir die Frage wie, bzw. wo ich die Tabelle am besten
> deklariere und initialisiere! Meine bisherige Lösung sieht in etwa wie
> folgt aus. Meine Frage ist allerdings ob man das Problem irgendwie
> besser lösen kann.

Vielleicht mit statischen Membervariablen. Dann merkt sich Deine Klasse 
im Konstruktor, die wievielte Instanz von ihr erzeugt wurde, und greift 
auf die entsprechende Zeile des config-Arrays zu:
1
/**
2
 * compile:
3
 *   g++ -std=c++11 -Os -Wall -Wextra -Wpedantic -o array array.cpp
4
 */
5
6
#include <iostream>
7
#include <iomanip>
8
#include <array>
9
#include <vector>
10
#include <stdexcept>
11
12
std::array<std::array<double, 5>, 6> data =
13
    {{ {{0.0, 0.1, 0.2, 0.3, 0.4}},
14
       {{1.0, 1.1, 1.2, 1.3, 1.4}},
15
       {{2.0, 2.1, 2.2, 2.3, 2.4}},
16
       {{3.0, 3.1, 3.2, 3.3, 3.4}},
17
       {{4.0, 4.1, 4.2, 4.3, 4.4}},
18
       {{5.0, 5.1, 5.2, 5.3, 5.4}} }};
19
20
class A {
21
public:
22
    A(void) {
23
        instance = instanceCounter;
24
        instanceCounter++;
25
        if(instanceCounter > data.size()) {
26
            throw std::runtime_error("Gak!");
27
        }
28
    }
29
    double foo(unsigned int n = 0) {
30
        std::cout << "instance: " << instance << ", "
31
                  << "instanceCounter: " << instanceCounter << ", "
32
                  << "n: " << n << ", "
33
                  << std::setprecision(1)
34
                  << std::fixed
35
                  << "data[0:4]: " << data[instance][0]
36
                  << ", " << data[instance][1]
37
                  << ", " << data[instance][2]
38
                  << ", " << data[instance][3]
39
                  << ", " << data[instance][4]
40
                  << std::endl;
41
        return 0.0;
42
    }
43
private:
44
    unsigned int instance;
45
    static unsigned int instanceCounter;
46
};
47
48
unsigned int A::instanceCounter = 0;
49
50
51
int main(void) {
52
53
    std::vector<A*> vec;
54
    for(int i = 0; i < 6; ++i) {
55
        vec.push_back(new A());
56
    }
57
58
    for(auto& v: vec) {
59
        v->foo();
60
    }
61
    
62
    return 0;
63
}

Eine andere Lösung steckt die Konfigurationszeilen in eine deque, pop()t 
sich im Konstruktor ihre Konfigurationszeile vom Anfang der deque (und 
push()t sie gegebenenfalls in Destruktor wieder hinten dran):
1
/**
2
 * compile:
3
 *   g++ -std=c++11 -Os -Wall -Wextra -Wpedantic -o deque deque.cpp
4
 */
5
6
#include <iostream>
7
#include <iomanip>
8
#include <vector>
9
#include <array>
10
#include <deque>
11
12
std::deque< std::array<double, 5> > config =
13
    {{ {{0.0, 0.1, 0.2, 0.3, 0.4}},
14
       {{1.0, 1.1, 1.2, 1.3, 1.4}},
15
       {{2.0, 2.1, 2.2, 2.3, 2.4}},
16
       {{3.0, 3.1, 3.2, 3.3, 3.4}},
17
       {{4.0, 4.1, 4.2, 4.3, 4.4}},
18
       {{5.0, 5.1, 5.2, 5.3, 5.4}} }};
19
20
class A {
21
public:
22
    A(void) {
23
        if(config.size() == 0) {
24
            throw std::runtime_error("Gak!");
25
        }
26
        myData = config.front();
27
        config.pop_front();
28
    }
29
    ~A(void) {
30
        config.push_back(myData);
31
    }
32
    double foo(unsigned int n = 0) {
33
        std::cout << "in a.foo(): n: " << n << ", "
34
                  << std::setprecision(1)
35
                  << std::fixed
36
                  << "config[0:4]: " << myData[0] << ", "
37
                  << myData[1] << ", " << myData[2] << ", "
38
                  << myData[3] << ", " << myData[4]
39
                  << std::endl;
40
        return 0.0;
41
    }
42
private:
43
    std::array<double, 5> myData;
44
};
45
46
47
int main(void) {
48
49
    std::clog << "config has now " << config.size() << " elements." << std::endl;
50
    
51
    std::vector<A*> vec;
52
    for(int i = 0; i < 6; ++i) {
53
        vec.push_back(new A());
54
        // call foo() here...
55
        //vec.back()->foo(1);
56
    }
57
58
    std::clog << "config has now " << config.size() << " elements." << std::endl;
59
60
    // ...or call foo() there
61
    for(auto& v: vec) {
62
        v->foo(2);
63
    }
64
65
    std::clog << "config has now " << config.size() << " elements." << std::endl;
66
67
    // make sure to call destructor
68
    while(vec.size() > 0) {
69
        A* a = vec.back();
70
        vec.pop_back();
71
        delete(a);
72
    }
73
74
    std::clog << "config has now " << config.size() << " elements." << std::endl;
75
76
    return 0;
77
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.