2015-07-13 15 views
5

Piszę następującą tablicę (klasę), która zwiększa rozmiar, gdy indeks tej tablicy jest większy niż rozmiar tej tablicy. Wiem o wektorach, ale musi to być tablica. Kod wygląda następująco:Klasa - Zdefiniowana przez użytkownika inteligentna tablica o rozmiarze dynamicznym

#include <iostream> 

using namespace std; 

class Array { 

public: 
Array():_array(new float[0]), _size(0){}; 
~Array() {delete[] _array;} 
friend ostream &operator<<(ostream&,const Array&); 
float& operator[] (int index) 
{ 
    if(index>=_size) 
    { 
    float* NewArray=new float[index+1]; 
    for(int i=0;i<_size;++i) NewArray[i]=_array[i]; 
    for(int i=_size;i<index+1;++i) NewArray[i]=0; 
    delete[] _array; 
    _array=NewArray; 
    _size=index+1; 
    } 
    return _array[index]; 
} 

private: 
    float *_array; // pointer to array 
    int _size; // current size of array 
}; 

ostream &operator << (ostream &out, const Array& obj) // overloading operator<< to easily print array 
{ 
    cout << "Array:\n\n"; 
    for (int i=0;i<obj._size;++i) 
    { 
    cout << obj._array[i]; 
    if(i+1!=obj._size) cout << ", "; 
    } 
    cout << ".\n"; 
    return out; 
} 

int main() 
{ 
    Array CustomArray; 
    CustomArray[2] = CustomArray[1] = CustomArray[0] = 3.14; // **here is the problem** 
    cout << CustomArray << endl; 
} 

Wszystko jest ok, 0 ostrzeżenia, 0 Valgrind błędy, Wyjście:

3.14, 3.14, 3.14. 

ALE mam napisać ten kod (w głównym) w ten sposób:

CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14; 

i teraz to 3 błędy Valgrind: Adres (some_address) wynosi 4 bajty wewnątrz bloku rozmiar 8 free'd,

i wyjście wygląda tak: 0, 0, 3.14.

niestety muszę napisać ten kod do pracy drugą drogę (CustomArray[0] = CustomArray[1] = CustomArray[2] = 3.14;) Można Chłopaki pomóc? Z góry dziękuję

+4

Problem polega na tym, że 'CustomArray [1]' unieważnia wszystkie poprzednie odniesienia i wskaźniki do danych, ponieważ jest ponownie przydzielany. Co możesz zrobić, to zwrócić 'ArrayAccess', który przechowuje indeks i nie posiada odwołania, ale uzyskuje dostęp do oryginalnej tablicy za każdym razem. – WorldSEnder

+2

Hmm, nie sądzę, że nawet 'std :: vector' może sobie z tym poradzić, jeśli jedno z tych wywołań operatora []' spowoduje realokację ... – cdhowie

Odpowiedz

2

Musisz rozwiązać ten problem za pomocą typu proxy, który zawiera odniesienie do obiektu Array, a indeks przekazany do Twojego operator[]. Ten typ proxy będzie domyślnie wymienialny na float i będzie można go przypisać z poziomu float, dzięki czemu dostęp (głównie) będzie przejrzysty.

W tym przypadku naruszamy również regułę trzech osób i zaimplementujemy operator przypisania kopiowania, aby przypisać wartość jednego elementu tablicy do drugiego, aby foo[0] = foo[1] działało zgodnie z oczekiwaniami.

Musimy wprowadzić następujące zmiany:

  1. Zmiana nazwy istniejącej operator[] i sprawiają, że prywatny; będzie używany tylko przez typ proxy.
  2. Utwórz nowy operator[], który zwraca wartość typu proxy.
  3. Napisz typ proxy.

Zmiana 1, wewnątrz definicji Array „s:

friend class ArrayElement; // So that ArrayElement can use access() 
private: 
float& access(int index) 
{ 
    if(index>=_size) 
    { 
    float* NewArray=new float[index+1]; 
    for(int i=0;i<_size;++i) NewArray[i]=_array[i]; 
    for(int i=_size;i<index+1;++i) NewArray[i]=0; 
    delete[] _array; 
    _array=NewArray; 
    _size=index+1; 
    } 
    return _array[index]; 
} 

Zmiana 2:

// Inside of Array 
public: 
    ArrayElement operator[](int index); 

// Implementation outside of Array 
ArrayElement Array::operator[](int index) { 
    return ArrayElement(*this, index); 
} 

Zmiana 3:

class ArrayElement 
{ 
    friend class Array; // So that Array can use our private constructor 

private: 
    ArrayElement(Array & array, int index) : array(array), index(index) { } 

public: 
    // Allows "foo[1] = 2" 
    ArrayElement const & operator=(float v) const { 
     array.access(index) = v; 
     return *this; 
    } 

    // Violation of the rule of three, but it makes sense in this case. 
    // Allows "foo[1] = foo[2]" 
    ArrayElement const & operator=(ArrayElement const & other) const { 
     array.access(index) = other; 
     return *this; 
    } 

    // Allows "float x = foo[1]" 
    operator float() const { 
     return array.access(index); 
    } 

private: 
    Array & array; 
    int index; 
}; 

(Minor ostateczna zmiana, trzeba do przekazania zadeklarować ArrayElement przed definicją z Array.)

See this working example.


jednym z zastrzeżeń od tej metody jest użycie typu wnioskowanie (auto C++ 11) na dostęp do tablicy:

auto x = an_array[1]; 

teraz x jest ArrayElement zamiast float a jego wartość zmieni się po zmianie an_array[1]. Próba przypisania innej wartości zmiennoprzecinkowej do x zmieni także wartość w an_array[1], ponieważ x jest po prostu proxy dla tej wartości.

Kontrast to do ogólnego zachowania std::vector gdzie auto x = a_vector[0] spowoduje x jest typ elementu wektora, a zatem będzie posiadać niezależną kopię wartości zapisanej w wektorze.

Należy jednak pamiętać, że specjalizacja std::vector<bool> jest zgodna z podejściem, które tu podałem (zwracając obiekt proxy), a więc it does have the same auto caveat! Możesz przyjąć to jako błogosławieństwo dla tego podejścia.

+0

Działa, świetnie! Ogromne dzięki dla ciebie, stary! – Handsomeguy123

0

Użyj bezpośrednio lub pośrednio std::vector. Twoje oświadczenie

Wiem o wektorach, ale musi to być tablica.

nie ma sensu. std::vector gwarantuje ciągłe przechowywanie, co prawdopodobnie oznacza "tablica". W przypadku instancji v zawsze można użyć wyrażenia &v[0], aby uzyskać adres bazowy tablicy, a począwszy od C++ 11 również będzie działał łatwiejszy do odczytania v.data(). Oznacza to, że można użyć numeru vector do wywoływania dowolnej funkcji, która wymaga tablicy "Styl C" jako wskaźnika i rozmiaru, np. qsort.

Jeśli nie można zrobić bez automatycznej zmiany rozmiaru w Array::operator [], a następnie dokonać klasy otoki, jak masz zrobić, ale używać std::vectorwewnętrznie. Jest to znacznie prostsze i bezpieczniejsze. W szczególności twój kod ma kwadratową, najgorszą wydajność, np. dodaje się będzie bardzo powolny:

Array CustomArray; 
for (int i = 0; i < 1000000; ++i) 
    CustomArray[i] = i; 

std::vector jest przeznaczony do nie mają tego problemu.

Drugi problem, o którym wspomniałeś, z unieważnieniem odwołania, może być łatwo rozwiązany za pomocą std::deque zamiast tego, jednak deque nie ma ciągłej pamięci. Dlatego z std::vector nadal musiałbyś używać proxy, jak to opisano w cdhowie. Muszę jednak przyznać, że nie do końca rozumiem, dlaczego składnia ma być taka, lub co jest nie tak z ręcznym wywołaniem std::vector<float>::resize().

Powiązane problemy