Problem mit Template für eine Mehrdimensionale dynamische Array

pointhi

Erfahrenes Mitglied
Hy,
ich arbeite heute schon den ganzen Tag daran eine Dynamische 2-Dimensionale Array mittel Template zu realisieren. Es ist meines Wissen möglich, und ich habe es geschafft so eine Array zumindestens lesbar zu machen. Wenn ich aber auf die Array schreiben möchte kommt folgende Fehlermeldung:

Code:
Als linker Operand einer Zuweisung wird L-Wert erfordert

Ich verstehe so in etwa was der L-Wert ist, weiß aber nicht wie ich den Code ändern muss damit es funktioniert.

Der Code ist von einem anderen Forum kopiert und auf Templates umgeschrieben worden

Der aktuelle Code ist das hier:
C++:
template <typename T>
class ArrayOfArrays {
public:
    ArrayOfArrays() {      // Konstruktor
        _arrayofarrays = new T*[10];   // Erzeuge 10x10 Array
        for(int i = 0; i < 10; ++i)
            _arrayofarrays[i] = new T[10];
    }

    class Proxy { // Hilfsklasse für die 2.Dimension
    public:
        Proxy(T* _array) : _array(_array) { }   // Konstruktor ->?<-

        T operator[](int index) {   // Operator um Wert von der 2. Dimension auszugeben
            return _array[index];
        }
    private:
        T* _array;   // Pointer für die Spalte
    };

    Proxy operator[](int index) {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }

private:
    T** _arrayofarrays; // Pointer für die 2D-Array
};

Die Kommentare sind von mir nachträglich hinzugefügt worden, dort wo ein ? steht bin ich mir nicht ganz sicher ob das genau die beschriebene Funktion ist.

In der Main-Funktion wird dementsprechend die Array erzeugt und geschrieben:

C++:
   ArrayOfArrays<int> TestArray;
   
   TestArray[0][0] = 200;  // Testwert
   TestArray[0][1] = 502;

Das Auslesen Funktioniert (bekomme uninitialiserte Werte, also pseudorandom :) ), nur das schreiben von Werten macht probleme.

Ich hab schon eine fertige Template bassierte Dynamische Array für 1-Dimension, die funktioniert heruntergeladen. Bei dieser ist der Teil der für den Aufruf vermutlich zuständig ist so deklariert:

C++:
T& operator [] (unsigned int index); // get array item

Bei dem mehrdimensionalen Programm dagegen so:

C++:
Proxy operator[](int index);

Ich schätze das dass & am Anfang den unterschied macht, aber im gegensatz zur 1-Dimensionalen Array wird der Typ Proxy, und nicht der von typename benutzt. Habt ihr eine Ahnung was ich machen muss damit ich auch in die Array schreiben kann?

mfg, pointhi
 
Zuletzt bearbeitet von einem Moderator:
Hallo pointhi,

Ersetze mal T durch int. Der proxy-Operator [] sieht jetzt folgendermassen aus:
C++:
int operator [] (int index) { ... }

Der Rückgabewert ist also eine Kopie ohne Namen (ein R-Wert). Diesem kannst du logischerweise nichts zuweisen. Wie du bereits richtig vermutet hast musst du mit Referenzen arbeiten. Das solltest du auch beim ArrayOfArrays-Operator [] und Proxy entsprechend machen, allerdings ist es da nicht lebenswichtig, da die ja intern mit Zeigern arbeiten.

Du solltest jeweils 2 operatoren machen:
C++:
T& Proxy::operator [] (int index) { }
const T& Proxy::operator [] (int index) const { }

und entsprechend bei ArrayOfArrays die operatoren auch einmal für konstante Objekte und einmal für non-const machen.

Lesen ist eine konstanzwahrende Operation und kann so auch auf const-Objekte angewandt werden.

Grüsse
Muepe
 
Danke, es funktioniert jetzt :)

C++:
template <typename T>
class ArrayOfArrays {
public:
    ArrayOfArrays() {      // Konstruktor
        _arrayofarrays = new T*[10];   // Erzeuge 10x10 Array
        for(int i = 0; i < 10; ++i)
            _arrayofarrays[i] = new T[10];
    }

    class Proxy { // Hilfsklasse für die 2.Dimension
    public:
        Proxy(T* _array) : _array(_array) { }   // Konstruktor ->?<-
        
         T& operator [] (int index) { return _array[index];}
         const T& operator [] (int index) const { return _array[index];}
    private:
        T* _array;   // Pointer für die Spalte
    };

    Proxy operator[](int index) {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }

private:
    T** _arrayofarrays; // Pointer für die 2D-Array
};

Jetzt sollte ich die eigentliche Klasse schreiben können, weil die grundlagen dafür geschafft sind. Hoffe das ich den rest selber schaffe :)

mfg, pointhi
 
Zuletzt bearbeitet von einem Moderator:
Du solltest das der Vollständigkeit und Sauberkeit halber auch bei:
C++:
Proxy operator [] (int index) { ... }

machen.
 
Stimmte es so?

C++:
    Proxy operator[](int index) {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }

    const Proxy& operator[](int index) const {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }

Blick bei diesen Funktionen noch nicht so ganz durch. War ganz verplüfft wie ich auf einmal diese Codeteile in meinen C++ Buch gesehen habe :).
 
Zuletzt bearbeitet von einem Moderator:
Also:

C++:
Proxy& operator[](int index) {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }

oder? Zumindestens Compiliert alles Fehlerfrei :)

Hy, es sind doch wieder ein paar probleme aufgetreten:

C++:
template <typename T>
class ArrayOfArrays {
public:
    ArrayOfArrays();      // Konstruktor

 
    class Proxy { // Hilfsklasse für die 2.Dimension
    public:
        Proxy(T* _array) : _array(_array) { }   // Konstruktor ->?<-
        
         T& operator [] (int index) ;

         const T& operator [] (int index) const;

    private:
        T* _array;   // Pointer für die Spalte
    };
 
    Proxy operator[](int index)
    {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }
 
    const Proxy& operator[](int index) const {   // Operator um Array Auszugeben und einzulesen ->?<-
        return Proxy(_arrayofarrays[index]);
    }
 
private:
    T** _arrayofarrays; // Pointer für die 2D-Array
};

template <typename T>
ArrayOfArrays<T>::ArrayOfArrays()
   {      // Konstruktor
   _arrayofarrays = new T*[10];   // Erzeuge 10x10 Array
   for(int i = 0; i < 10; ++i)
   _arrayofarrays[i] = new T[10];
   }

template <typename T>
T& ArrayOfArrays<T>::Proxy::operator [] (int index)
   {
   return _array[index];
   }

template <typename T>
const T& ArrayOfArrays<T>::Proxy::operator [] (int index) const
   {
   return _array[index];
   }

Ich versuche gerade die einzelnen, derzeit noch in der Klasse definierten Funktionen nach unten zu bewegen. Bei ein paar hat es funktioniert, nur ein paar nerven mich da richtig. Auch konnte ich
C++:
Proxy operator[](int index)
doch nicht mit einem & versehen, da ansonsten der Fehler
Code:
ungültige Initialisierung einer nicht-konstanten Referenz des Typs ?ArrayOfArrays<int>::Proxy&? von R-Wert des Typs ?ArrayOfArrays<int>::Proxy?
auftritt.

Erste Frage:

C++:
Proxy(T* _array) : _array(_array) { }

Das ganze schaut mir nach einen konstruktor aus. Was aber macht ": _array(_array) {}"? Dieser Programmierstiel ist mir unbekannt, und auch wie der konstruktor genau arbeitet ist mir unklar.

Zweite Frage:

C++:
const Proxy& operator[](int index) const;

Wie kann ich diesen Codeteil unter die Klasse schreiben? Ich hab mehrere methoden versucht, aber keine wurde akzeptiert:
Hier ein versuch davon:

C++:
template <typename T>
const Proxy& ArrayOfArrays<T>::operator [](int index) const
   {   // Operator um Array Auszugeben und einzulesen ->?<-
   return Proxy(_arrayofarrays[index]);
   }

Es kommt diese Meldung:
Code:
Fehler: ?Proxy? bezeichnet keinen Typ
Ich hab auch
C++:
const ArrayOfArrays<T>::Proxy& ArrayOfArrays<T>::operator [](int index) const
und andere möglichkeiten probiert. Aber jede hat andere Fehlermeldungen ausgespuckt.

Dritte Frage:

Derzeit kann ich es noch nicht testen, aber kann ich beim operator`[] aufruf auch diverse Funktionen aufrufen die z.b. die Array vergrößern, bzw. mittels if bei einem Bufferoverflow darauf reagieren. Oder ist nur das return zulässig?

Ich glaube das reicht erstmal.

mfg, pointhi
 
Zuletzt bearbeitet von einem Moderator:
Hallo pointhi

Bezüglich deinem ersten Fehler:
Das ist richtig, du erstellst bei jedem Aufruf des []-operators eine temporäre Variable und gibts eine Referenz darauf zurück, das ist nicht so gut. Wenn du C++11 verwendest empfehle ich Proxy mit einem move-Konstruktor zu versehen und dann auf die Referenz ganz zu verzichten. Ansonsten kannst du das Problem ganz beheben indem du nicht jedes mal ein Proxy objekt erstellst sonder indem du in Proxy ein Array von T der entsprechenden Dimension speicherst und in ArrayOfArrays ein Array von Proxy der entsprechenden Grösse, also sowas wie:
C++:
template<typename T>
class ArrayOfArrays
{
public:
    class Proxy
    {
     private:
            T* mArray;
            Proxy(const Proxy& ) { }
            Proxy& operator = (const Proxy& other) { return *this; }
     public:
            Proxy() : mArray(nullptr) {  }
            ~Proxy() { delete mArray; }

            T& operator [] (int index);
            const T& operator [] (int index) const;
            
            void init(int size) { delete []  mArray; mArray = new T[size]; }
    };

private:
    Proxy* mProxies;
public:
    ArrayOfArrays() {
           mProxies = new Proxy[10];
           for(int i = 0; i < 10; ++i) 
               mProxies[i].init(10);
    }

    Proxy& operator [] (int size);
    const Proxy& operator [] (int size) const;
};

template<typename T>
typename ArrayOfArrays<T>::Proxy& ArrayOfArrays<T>::operator [] (int index) {
        if(index >= 10)
            throw std::out_of_range("index >= 10");

        return mProxies[index];
}

template<typename T>
T& ArrayOfArrays<T>::Proxy::operator [] (int index) {
        if(index >= 10)
            throw std::out_of_range("index >= 10");

        return mArray[index];
}

Das sollte auch Fragen 2 und 3 beantworten. Du musst typename verwenden bei ArrayOfArrays<T>::proxy& weil es sich dabei um einen abhängigen Typen handelt und in C++ sicherheitshalber der Compiler alle abhängigen Typen als keine Typen sondern Werte betrachtet. Erst wenn du typename schreibst sagst du, dass er das nur als Typ auffassen soll.

Zu deiner ersten Frage:
Es handelt sich dabei um die Elementinitialisierungsliste, dazu findest du viel bei Google.

Grüsse
Cromon
 
Hy, es funktioniert immer besser, jetzt aber mal eine frage ob ich das ganze villeicht einfacher lösen kann.

  • Das ganze soll eine Dynamische Array werden
  • 2-Dimensional
  • Es besitzt eine Nullpunkt [0][0] von dem in alle Richtungen Speicher reserviert werden kann (also auch negativ, geht nach meinen recherchen und tests)
  • Sie muss wärend der Laufzeit erweiterbar sein
  • Ich benötige Fehlermeldungen, wenn kein freier Speicher mehr da ist, ich in ungültige adressen schreibe,...
  • Es muss beliebige Datentypen unterstützen

Um es zu erklären:

Das ganze kommt auf einem Rasperri Pi, der auf einem Roboter gebaut ist. Die Karte ist rechteckig, und wird von der 2D-Array repräsentiert. [0][0] ist der Startpunkt. Es gibt aber nicht nur 1. Array, sondern mehrere.

Die wichtigste hat die map gespeichert.
darauf kommen (referenziert auf [0][0]) neue dyn. arrays für diverse Algroithmen. Diese speichern die nötigen Daten in eigenen strukturen/klassen. Diese werden teilweise auch in eigenen Threads bearbeitet. Ich will das ganze nicht in eine einzige Array packen, bzw. kann es auch nicht.

Villeicht kennt ihr schon was passendes dafür? Ansonsten werde ich bei diesem Code weiterarbeiten müssen.

mfg, pointhi
 
Hab ein wenig herumprogrammiert. Ich hab es geschafft dass ich eine Flexible Array bekomme die in die positive Richtung funktioniert. Negative Werte sind auch möglich, aber irgendwie funktioniert das kopieren innerhalb der Templateklasse nicht.

C++:
/* 
 * File:   dynamicArray.h
 * Author: pointhi
 *
 * Created on 30. Jänner 2013, 21:12
 */

#ifndef ROBOCUP_DYNAMIC_ARRAY_H
#define	ROBOCUP_DYNAMIC_ARRAY_H

#include <iostream>

template<typename T>
class ArrayOfArrays
   {
   public:    
     class Proxy
         {
         private:
            T* mArray;
            unsigned int mSizeY;
            signed int *mOffsetY;
            Proxy(const Proxy& ) { }
            Proxy& operator = (const Proxy& other) { return *this; }     
         public:            
            Proxy() : mArray(NULL) { }            
            ~Proxy() { delete [] mArray; }             
            T& operator [] (int index);            
            const T& operator [] (int index) const;
            
            void init(int size, signed int *offset) { delete [] mArray; mArray = new T[size]; mSizeY= size; mOffsetY=offset;}
         }; 
     
         private:    
            Proxy* mProxies;
            unsigned int mSizeX, mSizeY;
            signed int mOffsetX, mOffsetY;
            
            void GenerateNewArrayOfArray(Proxy*& PointerArray, unsigned int ArraySizeX, unsigned int ArraySizeY);
            void DeleteArrayOfArray(Proxy*& PointerArray, unsigned int ArraySizeX);
         public:    
            int NewSize(unsigned int ArraySizeX, unsigned int ArraySizeY, signed int ArrayOffsetX, signed int ArrayOffsetY);
            ArrayOfArrays();    
            Proxy& operator [] (int size);    
            const Proxy& operator [] (int size) const;
   };

template<typename T>
ArrayOfArrays<T>::ArrayOfArrays()
   {
   this->mSizeX = 0; 
   this->mSizeY = 0;
   this->mOffsetX = 0;
   this->mOffsetY = 0;
   this->mProxies = NULL;
//   this->GenerateNewArrayOfArray(this->mProxies, 10, 10);
   }

template<typename T>
int ArrayOfArrays<T>::NewSize(unsigned int ArraySizeX, unsigned int ArraySizeY, signed int ArrayOffsetX, signed int ArrayOffsetY)
   {
   Proxy* HelpPointer;
   this->GenerateNewArrayOfArray(HelpPointer, ArraySizeX, ArraySizeY);
   
//   signed int OldOffsetX = this->mOffsetX;
//   signed int OldOffsetY = this->mOffsetY;
   
//   this->mOffsetX = 0;
//   this->mOffsetY = 0;
   
//   for(int x = 0; (x < ArraySizeX) && (x < this->mSizeX) ;x++)
//      {
//      for(int y = 0; (y < ArraySizeY) && (y < this->mSizeY) ;y++)
//         {
//         HelpPointer[x][y] = this->mProxies[x][y];
//         }
//      }

   std::cout << this->mProxies[1][1];     // ?, interner zugriff ****?, unbekannter Error wird ausgelöst
   
//   HelpPointer[1][1] = this->mProxies[1][1];
   
   this->DeleteArrayOfArray(this->mProxies, this->mSizeX);

   
   this->mProxies = HelpPointer;
   this->mSizeX = ArraySizeX;
   this->mSizeY = ArraySizeY;
   this->mOffsetX = ArrayOffsetX;
   this->mOffsetY = ArrayOffsetY;
   
   return 0;
   }

template<typename T>
typename ArrayOfArrays<T>::Proxy& ArrayOfArrays<T>::operator [] (int index)
   {        
   if(this->mSizeX <= index-this->mOffsetX || index-this->mOffsetX < 0) 
      {
      throw std::bad_alloc(); // Test error exception
//      throw std::out_of_range("index >= 10");   
      }      
   return mProxies[index-this->mOffsetX];
   }

template<typename T>
T& ArrayOfArrays<T>::Proxy::operator [] (int index)
   {        
   if(this->mSizeY <= (index-(*this->mOffsetY)) || (index-(*this->mOffsetY)) < 0) 
      {
      throw std::bad_alloc(); // Test error exception
      }
   return mArray[index-(*this->mOffsetY)];
   } 

template<typename T>
void ArrayOfArrays<T>::GenerateNewArrayOfArray(Proxy*& PointerArray, unsigned int ArraySizeX, unsigned int ArraySizeY)
   {
   PointerArray = new Proxy[ArraySizeX];
   for(int i = 0; i < ArraySizeX; ++i)
      {
      PointerArray[i].init(ArraySizeY, &this->mOffsetY);
      }
   }

template<typename T>
void ArrayOfArrays<T>::DeleteArrayOfArray(Proxy*& PointerArray, unsigned int ArraySizeX)
   {
   for(int i=0;i<ArraySizeX;i++)
      {
      delete &PointerArray[i];
      }
   delete PointerArray;
   PointerArray = NULL;
   }


#endif	/* ROBOCUP_DYNAMIC_ARRAY_H */

Das Problem liegt in der Funktion die die Größe verändern soll. Dazu müssen die einzelnen Elemente kopiert werden. Irgendwie kann ich aber nicht auf die Daten in der Pointer-Array zugreifen. Kann es sein dass intern ein anderer aufruf als "[][]" von nöten ist?

mfg, pointhi
 
Zuletzt bearbeitet von einem Moderator:

Neue Beiträge

Zurück