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

[C++] Obiekty w klasie trzymać jako wskaźniki czy zmienne automatyczne?

Ostatnio zmodyfikowano 2011-08-22 00:30
Autor Wiadomość
akwes
Temat założony przez niniejszego użytkownika
[C++] Obiekty w klasie trzymać jako wskaźniki czy zmienne automatyczne?
» 2011-08-21 00:49:03
Kurde, bo mi się woda z mózgu zrobiła już. Chyba zacząłem nadużywać wskaźników.

C/C++
// Hipotetyczna klasa,
// pisane z palca, nie kompilować :P

#include "obraz.h" // dla class Obraz;

class Galeria
{
    Galeria();
    Obraz ** obrazy;
};

Galeria::Galeria()
{
    int ILE;
    /* straszne dziwne obliczenia dla ILE */
    obrazy = new Obraz *[ ILE ];
   
    for( int i = 0; i < ILE; i++ )
         obrazy[ i ] = new Obraz();
   
}

C/C++
// Hipotetyczna klasa,
// pisane z palca, nie kompilować :P

#include "obraz.h" // dla class Obraz;

class Galeria
{
    Galeria();
    Obraz * obrazy;
};

Galeria::Galeria()
{
    int ILE;
    /* straszne dziwne obliczenia dla ILE */
    obrazy = new Obraz[ ILE ];
}

Klasa Galeria ma operować na Obrazach, nie są one przesyłane do funkcji ani do innych klas. Lepiej trzymać te zmienne jako wskaźnik (wolny operator new) czy jako tablicę obiektów (wtedy są po prostu to zmienne na stosie, takie szybsze?).

Nie jestem pewien czy drugi zapis jest naprawdę wydajniejszy od pierwszego. Ktoś rozwieje wątpliwości?

P-39593
malan
» 2011-08-21 10:59:27
C/C++
class Galeria
{
public:
    Galeria();
   
private:
    std::vector < Obraz > obrazy;
};
?

Nie jestem pewien czy drugi zapis jest naprawdę wydajniejszy od pierwszego
Zrób test :)
P-39595
akwes
Temat założony przez niniejszego użytkownika
» 2011-08-21 15:30:26
Własnie czy vectora używać też nie wiem, bo nie będę potrzebował zwiększać rozmiaru tablicy ani wykonywać skomplikowanych operacji i operacje wykonywały się będą zwykle na całej tablicy wiec iterator też mi nie da nic więcej.

No ale widzę czeka mnie testowanie :P
P-39608
malan
» 2011-08-21 22:55:52
Znasz rozmiar tablicy podczas kompilacji?
P-39659
akwes
Temat założony przez niniejszego użytkownika
» 2011-08-21 23:01:15
Nie znam.
Ale jej rozmiar ustalany jest raz przez konstruktor, potem nie ulega zmianie.

Sama tablica będzie dynamiczna i trzymana jako wskaźnik, tylko chodzi o to czy ma przechowywać obiekty automatyczne czy wskaźniki do nich.

Czy szybsze jest Cos[0]->zmienna, czy Cos[0].zmienna;
i jaka różnica w czasie tworzenia zmiennych przez:

C/C++
for( int i = 0; i < ILE; i++ )
     obrazy[ i ] = new Obraz();
// każdy wskaźnik dostaje adres zmiennej typu Obraz w pamięci.

C/C++
obrazy = new Obraz[ ILE ];
// czyli po prostu wstaw nową ILE-elementową tablicę typu Obraz, obiekty się powinny stworzyć same przez konstruktor domyślny.

Efekt końcowy ten sam, tylko inaczej się odnoszę "->" albo ".". Nie muszę obiektów wysyłać do funkcji ani nigdzie, korzysta z nich tylko klasa.
P-39661
malan
» 2011-08-21 23:02:35
Też mam ten sam problem ;p Zawsze sobie mówię, że zrobię jakieś testy, ale jakoś zapominam ;p
P-39662
akwes
Temat założony przez niniejszego użytkownika
» 2011-08-21 23:05:37
Bo teoretycznie zmienne automatyczne trzymane są na stosie i na stos się szybko operuje (czyli szybko wkłada na stos przy ich tworzeniu). Zaś ponoć "new" jest wolny i przy wskaźnikach trzeba pamiętać o ich niszczeniu przy niszczeniu obiektu klasy...

Mówię ponoć i teoretycznie, nigdy tego nie sprawdzałem a bazuje na jakieś tam wiedzy z różnych forum, gdzie ludzie też pewnie pisali "na oko".
P-39663
akwes
Temat założony przez niniejszego użytkownika
» 2011-08-22 00:30:37
Przepraszam że double post, ale uznałem że dość ważne wniesienie do tematu:

Czy ktoś mógłby potwierdzić czy te testy mają jakąkolwiek wartość? Czytając oczywiście również podsumowanie.

1. TEST ZMIENNYCH AUTOMATYCZNYCH NA TYPIE WBUDOWANYM
C/C++
#include <SFML/System.hpp>
#include <iostream>
#include <conio.h>


int main()
{
    sf::Clock Clock;
   
    int ILE = 100;
   
    // zrobimy sobie to 100 razy.
    for( int j = 0; j < 100; j++ )
    {
        // TEST AUTOMATYCZNYCH
        Clock.Reset();
        // 1000 Inicjalizacji
        for( int k = 0; k < 1000; k++ )
        {
            int * TabWsk;
            TabWsk = new int[ ILE ];
            for( int i = 0; i < ILE; i++ )
            {
                TabWsk[ i ] = 3;
            };
        }
        std::cout << "AUTOMATYCZNE: Czas 1000 inicjalizacji = " << Clock.GetElapsedTime() << '\n';
    }
    _getch();
    return 0;
}

Czasy są rzędu: 0,0014

2. TEST ZMIENNYCH DYNAMICZNYCH NA TYPIE WBUDOWANYM
C/C++
#include <SFML/System.hpp>
#include <iostream>
#include <conio.h>


int main()
{
    sf::Clock Clock;
   
    int ILE = 100;
   
    for( int j = 0; j < 100; j++ )
    {
        // TEST WSKAŹNIKÓW
        Clock.Reset();
        // 1000 Inicjalizacji
        for( int k = 0; k < 1000; k++ )
        {
            int ** TabWsk;
            TabWsk = new int *[ ILE ];
            for( int i = 0; i < ILE; i++ )
            {
                TabWsk[ i ] = new int( 3 );
            };
        }
        std::cout << "WSKAZNIKI: Czas 1000 inicjalizacji = " << Clock.GetElapsedTime() << '\n';
    }
    _getch();
    return 0;
}

Czasy są rzędu: 0,076

2.1 MAŁE WTRĄCENIE VECTORA
C/C++
int main()
{
    sf::Clock Clock;
   
    int ILE = 100;
   
    // zrobimy sobie to 100 razy.
    for( int j = 0; j < 100; j++ )
    {
        // TEST AUTOMATYCZNYCH
        Clock.Reset();
        // 1000 Inicjalizacji
        for( int k = 0; k < 1000; k++ )
        {
            std::vector < int > TabWsk( ILE );
            for( int i = 0; i < ILE; i++ )
            {
                TabWsk[ i ] = 3;
            };
        }
        std::cout << "AUTOMATYCZNE: Czas 1000 inicjalizacji = " << Clock.GetElapsedTime() << '\n';
    }
    _getch();
    return 0;
}
Daje czasy rzędu: 0.010

3. ZAMIAST WBUDOWANEGO OBIEKTU ZOSTANIE UŻYTY OBIEKT DEFINIOWANY PRZEZ UŻYTKOWNIKA O POSTACI:
C/C++
class Obiekt
{
public:
    Obiekt() { };
};

Dla wskaźników:
Czasy są rzędu: 0,079
Dla zmiennych automatycznych:
Czasy są rzędu: 0,0033

Jeśli chodzi o czas tworzenia, to zmienne automatyczne są faktycznie dużo szybsze. To rowiazuje mój problem.

3. ODWOŁANIE SIĘ PRZEZ WSKAŹNIK DO SKŁADNIKA KLASY
C/C++
Obiekt * wsk = new Obiekt( 5 );
Obiekt * wsk2 = new Obiekt( 10 );
for( int j = 0; j < 100; j++ )
{
    Clock.Reset();
    for( int k = 0; k < 1000000; k++ )
    {
        wsk->a = wsk2->a;
    }
    std::cout << "WSKAZNIKI: Czas 1000000 inicjalizacji = " << Clock.GetElapsedTime() << '\n';
}

Czas: od 0.086 -> ~0.020
(zaczyna się od 0.086, potem systematycznie spada i utrzymuje się na poziomie 0.015 - 0.025;

4. ODWOŁANIE SIĘ PRZEZ OPERATOR WYŁUSKANIA DO SKŁADNIKA KLASY
C/C++
Obiekt ob( 5 );
Obiekt ob2( 10 );

for( int j = 0; j < 100; j++ )
{
    Clock.Reset();
    for( int k = 0; k < 1000000; k++ )
    {
        ob.a = ob2.a;
    }
    std::cout << "OPERATOR WYL: Czas 1000000 inicjalizacji = " << Clock.GetElapsedTime() << '\n';
}
Czas: od 0.086 -> ~0.020

5. Podsumowanie.

Jeżeli jakaś zmienna, tablica czy obiekt definiowany przez użytkownika jest tworzony jedynie na potrzeby klasy to lepiej go zrobić automatyczną zmienną a nie wskaźnikiem. Wskaźniki używać dopiero przy przesyłaniu do funkcji. Czyli innymi słowy unikać operatora new.
C/C++
Obiekt Ob1();
Obiekt * wsk = & Ob1;
To dużo lepszy pomysł niż
C/C++
Obiekt * Ob1 = new Obiekt();
O jakieś 50 razy.

Nie wiem czy inni tak mają. Ale ja wpadłem chyba w wskaśnikomanie, co tylko się dało robiłem jako dynamicznie, prawie każdy element klasy. To jest zła praktyka, dynamicznej alokacji należy używać tylko wtedy gdy ilość obiektów jest nieznana(lub zmienna) przez pewien czas pracy programu. Operator new jest faktycznie wolny.

Może to co mówię jest oczywiste, jednak ja dopiero teraz zauważyłem jak dużą część kodu mam głupio zrobioną :P

Jeśli chodzi o odnoszenie się przez "." lub "->" nie wynika by któreś było szybsze.
P-39665
« 1 »
  Strona 1 z 1