Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Problem: obiekt klasy szablonowej zawiera inny obiekt klasy szablonowej

Ostatnio zmodyfikowano 2012-10-20 19:46
Autor Wiadomość
Neth
Temat założony przez niniejszego użytkownika
Problem: obiekt klasy szablonowej zawiera inny obiekt klasy szablonowej
» 2012-10-14 11:47:30
Witam.

Stworzyłem sobie szablon klasy SmartArray, który służy do obsługi dynamicznych tablic dowolnego typu.
Mam też klasę CFoo oraz CBar. CFoo zawiera dwa numerki, a CBar - m.in. obiekt klasy SmartArray<CFoo> Foo. W programie tworzę obiekt SmartArray<CBar> Bar, a więc mam tablicę obiektów CBar, które zawierają po jednej tablicy obiektów CFoo.

Wszystko działa świetnie.
Problem pojawia się, gdy chcę rozszerzyć Bar. Pola num każdego obiektu tablicy CBar.Array są ok, ale Foo przyjmuje wtedy dziwne wartości i przy próbie ponownego poszerzenia Bar lub zniszczenia Bar wyskakuje błąd o odczytywaniu niedozwolonego miejsca w pamięci.

Stworzyłem nawet specjalizację metody extend_by() dla obiektów CBar, ale błąd jest podobny - już przy próbie pierwszego rozszerzenia kompilator twierdzi, że wskaźnik Foo.Array pokazuje na 0x00000000 i nie można się do tego dostać.

Dlaczego tak się dzieje? Jak można to naprawić/ominąć? Z góry dzięki za pomoc.

Programuję w Visual C++ 2010 Express. Poniżej zamieszczam potrzebny kod.


C/C++
class CFoo
{
public:
    int num, num2;
   
    CFoo()
        : num( 0 )
        , num2( 2 )
    {
       
    }
    ~CFoo()
    {
       
    }
};

C/C++
class CBar
{
public:
    int num;
    SmartArray < CFoo > Foo;
   
    CBar()
        : num( 5 )
    {
       
    }
    ~CBar()
    {
       
    }
};

C/C++
template < typename Type > class SmartArray
{
private:
    static unsigned DefaultSize, DefaultAddition, DefaultMultiplier;
   
    unsigned Size;
    Type * Array;
   
public:
    [...]
    explicit SmartArray( std::string ArrayType, unsigned ArraySize = DefaultSize )
        : TypeName( ArrayType )
        , Size( ArraySize )
        , Array( new Type[ Size ] )
    {
       
    }
   
    template < typename OtherType > SmartArray < Type >& operator =( const SmartArray < OtherType >& )
    {
        Size = OtherArray.size();
       
        // usuwam obecną tablicę i tworzę nową o nowym rozmiarze
        delete[] Array;
        Array = new Type[ Size ];
       
        // przypisuję nowej tablicy wartości innej tablicy
        for( unsigned I = 0; I < Size; I++ )
             Array[ I ] = OtherArray[ I ];
       
        return * this;
    }
   
    bool extend_by( unsigned Addition = DefaultAddition )
    {
        if( Addition == 0 ) return false;
       
        // Tworzę przejściową tablicę o nowym rozmiarze i przypisuję jej wartości obecnej tablicy
        Type * Buf = new Type[ Size + Addition ];
        for( unsigned I = 0; I < Size; I++ )
             Buf[ I ] = Array[ I ];
       
        // Usuwam obecną tablicę i przypisuję jej wskaźnik do nowej
        delete[] Array;
        Array = Buf;
        Size += Addition;
        return true;
    }
    [...]
};

Specjalizacja extend_by() dla CBar:
C/C++
template <> bool SmartArray < CBar >::extend_by( unsigned Addition )
{
    if( Addition == 0 ) return false;
   
    CBar * Buf = new CBar[ Size + Addition ]();
    for( unsigned I = 0; I < Size; I++ )
    {
        Buf[ I ].num = Array[ I ].num;
        for( unsigned S = 0; S < Array[ I ].Foo.size(); S++ )
        {
            Buf[ I ].Foo[ S ].num = Array[ I ].Foo[ S ].num;
            Buf[ I ].Foo[ S ].num2 = Array[ I ].Foo[ S ].num2;
        }
    }
    delete[] Array;
    Array = Buf;
    Size += Addition;
    return true;
}
P-66800
Mrovqa
» 2012-10-14 13:59:18
Zawsze dawaj log z kompilacji - to zdecydowanie przyspieszy proces analizy problemu przez innych.
C/C++
template < typename Type, typename OtherType > class SmartArray // tu musimy oznaczyć jaka to konkretnie ma być klasa*
{
private:
    static unsigned DefaultSize, DefaultAddition, DefaultMultiplier;
   
    unsigned Size;
    Type * Array;
   
public:
    //...
    explicit SmartArray( std::string ArrayType, unsigned ArraySize = DefaultSize )
        : TypeName( ArrayType )
        , Size( ArraySize )
        , Array( new Type[ Size ] )
    {
       
    }
   
    /*template < typename OtherType >*/ // każda metoda jest szablonem! Nie można zdefiniować szablonu szablonu.
    SmartArray < Type >& operator =( const SmartArray < OtherType >& )
    {
        //...
    }
   
    //...
};
* Kompilator na podstawie szablonu sobie generuje nową klasę, jeśli tego trzeba. Mój sposób powinien zadziałać, co do Twojego myślenia - coś takiego ma sens i IMHO powinno być dozwolone... z szablonów dużo nie korzystam, więc nie jestem pewny Twojego rozwiązania - ale swojego na 99% już tak.
Daj log kompilacji, nie będzie "wróżenia z niczego".
P-66815
DejaVu
» 2012-10-14 15:11:51
Powinieneś mieć napisany konstruktor kopiujący jeżeli alokujesz dynamicznie pamięć.
P-66823
Neth
Temat założony przez niniejszego użytkownika
» 2012-10-14 18:02:39
Dzięki za odpowiedź. Przetestuję to i przemyślę. Wtedy ewentualnie dodam log z kompilacji.
Mam zadeklarowane wszystkie potrzebne funkcje, m.in. także konstruktor kopiujący, ale nie dodawałem, go bo w moim problemie (chyba) nie jest używany.

Dopóki się nie rozwiąże, nadal czekam na pomysły innych, jeżeli macie.
P-66834
Neth
Temat założony przez niniejszego użytkownika
» 2012-10-14 19:34:26
Gdy OtherType wstawię do listy parametrów w nagłówku szablonu to będzie się on domagał wszystkich parametrów szablonu przy tworzeniu każdego obiektu, a to nie tak ma działać. OtherType był potrzebny aby umożliwić przypisanie np. tablicy intów do floatów.
Nie zależy mi na tym teraz, więc usunąłem części kodu z OtherType i zostawiłem to w tej formie:

C/C++
template < typename Type > class SmartArray
{
    [...]
    SmartArray & operator =( const SmartArray & )
    {
        Size = OtherArray.size();
       
        delete[] Array;
        Array = new Type[ Size ];
       
        for( unsigned I = 0; I < Size; I++ )
             Array[ I ] = OtherArray[ I ];
       
        return * this;
    }
    [...]
}
Ale błąd pojawia się nadal.

Mój problem pojawia się podczas działania programu, a nie w czasie kompilacji. Oto informacje o błędach z Output w czasie działania programu:

First-chance exception at 0x5e1557aa (msvcr100d.dll) in Program obiektowy SFML.exe: 0xC0000005: Access violation reading location 0xfdfdfdf1.
Unhandled exception at 0x5e1557aa (msvcr100d.dll) in Program obiektowy SFML.exe: 0xC0000005: Access violation reading location 0xfdfdfdf1.

I używając specjalizacji extend_by() dla CBar:
First-chance exception at 0x0022505d in Program obiektowy SFML.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x0022505d in Program obiektowy SFML.exe: 0xC0000005: Access violation reading location 0x00000000.

Przepraszam za pisanie postu pod postem.. Pomyliłem się i nie mogę usunąć nowego.
P-66837
Neth
Temat założony przez niniejszego użytkownika
» 2012-10-20 12:05:19
Odświeżam temat.

Czy nikt nie ma pojęcia o co może tutaj chodzić? Czy może to być wina kompilatora czy gdzieś w kodzie jednak jest błąd?
P-67116
Admixior
» 2012-10-20 14:22:10
Pokażesz jak tworzysz tę klasę.

static unsigned DefaultSize, DefaultAddition, DefaultMultiplier; // gdzie to inicjalizujesz?

oraz zdefiniuj sobie konstruktor bez parametrów w sekcji private (tak dla pewności)

P-67121
Mrovqa
» 2012-10-20 14:25:17
Mam zadeklarowane wszystkie potrzebne funkcje, m.in. także konstruktor kopiujący, ale nie dodawałem, go bo w moim problemie (chyba) nie jest używany.
Gdy jesteś zmuszony do zdefiniowania destruktora, konstruktora kopiującego lub operatora przypisania to zazwyczaj jest się zmuszonym do napisania wszystkich powyższych metod (a w szczególności, gdy korzysta się z dynamicznej alokacji pamięci). Poza tym - skoro nie wiesz czy jest używany konstruktor kopiujący, to dlaczego go nie zdefiniujesz (dużo roboty tu nie ma), tylko z góry zakładasz, że nigdzie nie jest wywoływany? Taki konstruktor jest wywoływany m.in. gdy wysyłasz obiekt poprzez wartość do funkcji czy czasem (z powodu optymalizacji) przy tworzeniu obiektu używasz operatora przypisania.

/edit:
oraz zdefiniuj sobie konstruktor bez parametrów w sekcji private (tak dla pewności)
Niepubliczny konstruktor domyślny to nie jest najlepszy pomysł - wtedy nie można napisać tak:
klasa nazwa;
.
P-67122
« 1 » 2
  Strona 1 z 2 Następna strona