Nowe posty

Autor Wątek: [C++]Kilka pytań  (Przeczytany 5133 razy)

Offline W K

  • Users
  • Nowy na forum
  • *
  • Wiadomości: 46
    • Zobacz profil
[C++]Kilka pytań
« dnia: 2007-12-04, 22:11:29 »
Witam!

Jestem w trakcie nauki C++. W trakcie nauki pojawilo mi sie kilka pytan,
moze mi podpowiecie?

1. Znalazlem informacje o funkcjach czysto wirtualnych. Ich istota jest dla
mnie zupelnie zrozumiala: wymuszaja one ich nadpisanie w klasie potomnej i
jednoczesnie uniemozliwiaja tworzenie klasy bazowej. Tym bardziej dla mnie
jest niezrozumiale, dlaczego autor ksiazki z ktorej sie ucze (Jesse Liberty,
C++ w 24 godziny) pokazuje, ze mozna taka funkcje zaimplementowac w klasie
bazowej, po czym nie implementuje jej (nie nadpisuje) w klasie potomnej. O
ile rozumiem, taki zabieg powoduje, ze zachowuje sie ona jak zwykla funkcja
wirtualna, wiec po co ten cyrk z ustawianiem jej jako czysto wirtualna?
Chyba troche to tlumaczenie zakrecilem wiec zapytam inaczej: czy (i jakie)
sa powody implementowania funkcji czysto wirtualnych?

2. Do czego tak naprawde moze byc przydatna funkcja (operator - co to jest w
ogole) dynamic_cast? Z ksiazki zrozumialem, ze jesli zwraca ona null jesli
"castowany" za jej pomoca jest obiekt klasy roznej od typu wskaznika, albo
wskaznik na ten obiekt, jesli typ sie zgadza:
[KOD]
Kot *wsk=dynamic_cast < Kot *> (obiekt_1) //i po tym zabiegu wsk=null bo
obiekt_1 jest klasy Pies
Kot *wsk=dynamic_cast < Kot *> (obiekt_2) //i teraz wsk wskazuje na obiekt
klasy Kot, bo obiekt_2 jest klasy Kot
[/KOD]
Czy dynamic_cast sluzy tylko do sprawdzania typu obiektu?

3. Typ wyliczeniowy: dzialanie jest dla mnie jasne. Jedno pytanie tylko: po
co do niego przypisywac cyfry?
(Tj: enum kolor {CZERWONY, ZIELONY=5, NIEBIESKI, CZARNY=10, ZOLTY=20})
Przeciez i tak nie moge napisac
kolor zmienna=2;
ani tez jesli
int funkcja(kolor);
nie moge jej wywolac funkcja(3)??

4. Ostatnie pytanie:
Wskazniki tworzone sa w wolnym obszarze pamieci, na stercie. Zwykle zmienne
na stosie. Jesli pamieci braknie, to wskaznik tworzony jako new int (np.)
bedzie mial wartosc NULL (co ulatwi obsluge bledow) - jesli przepelni sie
stos, program sie wykrzaczy. Z drugiej strony wygodniejsze (i chyba
polecane) do uzycia sa referencje. Zatem czy nie lepiej jest zamiast tworzyc
normalne zmienne, robic wskazniki i potem do ich zawartosci tworzyc
referencje? Pamiec w obu przypadkach i tak bedzie zwalniana z zakonczeniem
funkcji/programu?

Dodam, że to nie ma nic wspólnego z odrabianiem zadań domowych ;)

Najbardziej zależy mi na zrozumieniu tego enum-a, bo nie daje mi spać po nocach...
Z gory dzieki za odpowiedz, pozdrawiam!

Offline xis

  • Global Moderator
  • Guru
  • *****
  • Wiadomości: 1049
    • Zobacz profil
[C++]Kilka pytań
« Odpowiedź #1 dnia: 2007-12-05, 07:38:50 »
Jeśli chodzi o Enum, to na mój chłopski rozum, taka możliwość pomaga np. z sortowaniu tablic wyliczanych za pomocą takich wartości, albo np. w kompatybilności między środowiskami; gdy np. jakaś zmienna równa się wartości stałej typu wyliczeniowego i zapisujesz ją do pliku, a inny program (być może w innym języku) ma taką zmienną odczytać.
No i na koniec najbardziej oczywista rzecz: czasami definiujemy stałe wyliczeniowe po to by oddać jak najwierniej jakiś fragmen rzeczywistości. Ale czasem możemy chcieć zaimplementować tylko część tego fragmentu, np.:
SWIATLO_CZERWONE=0, SWIATLO_ZIELONE=2
... zostawiamy sobie miejsce dla ewentualnego światła pomarańczowego (jeśli kiedyś zechcemy je implementować).

Co do reszty Twoich pytań: jak bardzo jestem szczęśliwy, że nie muszę już pisać w C++...

http://www.kdedevelopers.org/node/2298 ;)
Everything should be made as simple as possible, but not simpler
-- Albert Einstein

Offline Piotr Chmura

  • Administrator
  • Guru
  • *****
  • Wiadomości: 5060
    • Zobacz profil
[C++]Kilka pytań
« Odpowiedź #2 dnia: 2007-12-05, 09:11:42 »
Ad. 1

Cytuj
ze mozna taka funkcje zaimplementowac w klasie
bazowej, po czym nie implementuje jej (nie nadpisuje) w klasie potomnej
Próbowałeś coś takiego zrobić ?

Klasa bazowa nadal jest abstrakcyjna, bo ma funkcję czysto wirtualną, i TRZEBA ją zaimplementować w klasie pochodnej - inaczej nie będzie można stworzyć obiektu.
Natomiast nic nie stoi na przeszkodzie, żeby zaimplementować ją również w klasie bazowej i np. wywołać z klasy pochodnej, np.:

class A {                                                                       
protected:                                                                      
    virtual void aa()=0;                                                        
};                                                                              
                                                                               
void A::aa() {};                                                                
                                                                               
class B : public A {                                                            
    void aa() { A::aa(); }                                                      
};                                                                              
                                                                               
                                                                               
int main (int arg, char ** argv){                                              
//  A a; // obiektu 'a' nie można utworzyć ze względu na czysto wirtualną funkcję aa()
    B b;                                                                        
    return 0;                                                                  
}
Ad. 2
dynamic_cast służy do rzutowania 'w dół' hierarchii klas, tzn. mając wskaźnik do klasy bazowej możesz za jego pomocą, w bezpieczny sposób, otrzymać wskaźnik do klasy pochodnej.

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/keyword_dynamic_cast.htm

Ad. 3
Typ wyliczeniowy jest trzymany na wartości int. Przypisywanie wartości daje ci większa kontrolę nad nim. Możesz na przykład trzymać sobie na nim maski bitowe i w wygodny sposób z nich korzystać.
enum blabla {
    PIERWSZY_BIT = 1,
    DRUGI_BIT = 1 << 1,
    TRZECI_BIT = 1 << 2,
    CZWARTY_BIT = << 3
}
(...)
int moja_wartosc = PIERWSZY_BIT | CZWARTY_BIT;
Tak, to się przydaje ;-)
Kod jest bardziej przejrzysty. Oczywiście nazwy PIERWSZY_BIT itd. są tylko na potrzeby przykładu, lepiej stosować nazwy mające jakiś związek z tym, co reprezentują, a nie z tym, jaką mają wartość.

Ad. 4
Cytuj
Zatem czy nie lepiej jest zamiast tworzyc
normalne zmienne, robic wskazniki
Chciałem zauważyć, że wskaźnik to również jest zmienna (przechowuje adres pamięci), a tym samym zajmuje miejsce (na stosie lub na stercie, zależnie jak jest tworzona)
Cytuj
i potem do ich zawartosci tworzyc
referencje? Pamiec w obu przypadkach i tak bedzie zwalniana z zakonczeniem
funkcji/programu?
Tutaj nie bardzo wiem o co ci chodzi... możesz przedstawić przykład ?

Nie komplikuj sobie za bardzo życia ;-)

Edit: dopisanie komentarza o nazwach wartości enuma w przykładzie.

Offline

  • Users
  • Nowy na forum
  • *
  • Wiadomości: 32
    • Zobacz profil
[C++]Kilka pytań
« Odpowiedź #3 dnia: 2007-12-13, 10:20:34 »
A propos Jessie Liberty obecnie zajmuje się wyłącznie c# i platformą .NET. Jego doświadczenia z C++ opierają się na oprogramowaniu Microsoft, nie jest więc najlepszym nauczycielem programowania w linux. Poza tym czytałem ostatnio jego książkę "Programowanie w C#" , a niedługo po tym "Thinking in Java" Bruce Eckel'a i naprawdę widzę ogromną różnicę. Niektóre elementy C# zrozumiałem dopiero po przeczytaniu "Thinking in Java" :-)   Według mnie lepiej uczyć się z "Symfoni C++" lub "Thinking in C++". Sam zaczynałem naukę C++ od Porębskiego to dopiero była męczarnia :)
Jeżeli nie chcesz zwalniać pamięci po użyciu operatora new to przerzuć się na Jave lub C#. Tam tego nie musisz robić. Natomiast w c++ sam system operacyjny może ale nie musi zwolnić pamięci po zakończeniu programu, a już na pewno nie zwolni jej w trakcie wykonywania. Jeżeli chciałbyś nie używać operatora new to nie będziesz w stanie zaalokować dużych obiektów.

Offline

  • Users
  • Prawie jak Guru
  • ****
  • Wiadomości: 434
    • Zobacz profil
[C++]Kilka pytań
« Odpowiedź #4 dnia: 2007-12-13, 19:32:48 »
Ad.4
Wskaźniki są lepsze w jednych przypadkach, referencje w innych.
Co do wskaźników, jeśli zadeklarujesz jakiś, to musisz mu przypisać adres, bo inaczej nie będzie na nic wskazywał (a ściślej mówiąc - będzie wskazywał na przypadkowe miejsce w pamięci). Referencja to co innego - idzie ją utworzyć tylko w odniesieniu do istniejącego obiektu (lub zmiennej).

Poza tym w C++ już nie trzeba używać słowa NULL (chociaż można). W języku C słowo NULL oznaczało wskaźnik typu void na adres 0. W C++ jest to po prostu zwyczajne 0. Więc zamiast NULL w C++ można bez obawy pisać 0.

Co do zwalniania pamięci - w C++ jeśli zaalokujesz pamięć (np. utworzysz jakiś obiekt, jakąś tablicę, itd), za pomocą operatora new, to gdy już tego nie potrzebujesz (znaczy tego obiektu, tablicy, ...), to musisz usunąć go za pomocą operatora delete. C++ to nie Java - nie usuwa dynamicznych obiektów sam.
I należy pamiętać - alokacja dynamiczna tablicy:
int *tablica = new int[10];
usunięcie:
delete[] tablica;
alokacja dynamiczna pojedynczego obiektu:
int *wskaznik = new int;
usunięcie:
delete wskaznik;
czyli należy pamiętać kiedy stosować nawiasy prostokątne, a kiedy nie