[C++] utworzenie obiekty typu tablica char


(Ziomek Zemsty) #1

Witam.

Mam nietypowy problem. W programie, który obecnie piszę, mam klasę nazwijmy ją QWERTY. Potrzebuję utworzyć  dwa obiekty, które będą defacto tablicami znaków. Czyli robię:

...

int main()
{
    QWERTY *napis1;
    QWERTY *napis2;
    ...
}

a z kolei w pliku z rozszerzeniem .h, gdzie jest deklaracja klasy ląduje wtedy, o ile dobrze rozumiem:

class QWERTY
{
    private:
        char *cos_tam1=new char[];
        char *cos_tam2=new char[]; /*czy w nawiasach powinno sie cos znalezc?*/
        
        ...
};

Chciałbym też za pomocą jakiejś metody wczytywać dane do tych łańcuchów, tj. maję one być pobierane od użytkownika. 

Jeśli dobrze rozumiem, to wówczas wszelkie odwołania do obiektu będą wyglądały na zasadzie napis1.cos_tam1 ...?

Wybaczcie chaos, ale nie mam bladego pojęcia, jak to zrobić. Co umieścić w mainie, a co w sekcji private klasy? 


(Fizyda) #2

Gdy masz wskaźnik używasz innego operatora wyłuskania, nie kropki tylko ‘->’.

Czyli to będzie tak:

napis->wczytaj();

Tak samo masz przy this, this jest słowem kluczowym - wskaźnikiem na obecny obiekt. Gdy masz taką sytuację:

QWERTY obj();

w tedy metody wywołujesz tak:

obj.wczytaj();

 


(Ziomek Zemsty) #3

A konieczne jest podawanie rozmiaru w nawiasach? I czy w ogóle to co napisałem, żeby nakreślić problem, jest poprawne? Jak mogę “dopasować” tablicę do wpisywanego tekstu?


(Fizyda) #4

Nie rozumiem sensu twojego przykładu szczerze mówiąc, dodatkowo jest on niepoprawny. Masz 2 sposoby na stworzenie tablicy, albo dynamicznie, albo nie (chyba statycznie) pierwszy wymaga używania wskaźników drugi nie, ale w drugim musisz znać w trakcie kompilacji długość tablicy. W pierwszym dodatkowo musisz zwolnić zarezerwowaną pamięć podczas tworzenia danej tablicy, gdy kończysz pracę z nią, lub w przypadku obiektów w destruktorze zwalniasz dynamicznie zarejestrowaną pamięć - przy pomocy operatora new. Usuwasz przez delete.

Jeśli wiesz że tablica ma mieć 10 elementów to tworzysz ją tak:

char costam[10];

i teraz do poszczególnych elementów odnosisz się normalnie:

for (int i = 0; i < 10; i++)
    cout << costam[i];

Gdy nie znasz długości tablicy to tworzysz ją tak:

int a_size = 10; // dynamiczna długość np jako argument funkcji

char *costam; // wskaznik na obiekt typu char
costam = new char[a_size]; // utworzenie tablicy

i teraz przykład użycia:

for (int i = 0; i < a_size; i++)
    cout << costam[i]; // notacja tablicowa, notacja wskaznikowa to *(costam+i)

Po zakończeniu pracy z tablicą, gdy już więcej nie będziesz jej używał musisz ją usunąć bo dynamicznie ją stworzyłeś i musisz zwolnić pamięć:

delete [] costam;

Ostatnia rzecz, to co masz w main nic nie robi, są to tylko 2 wskaźniki które na nic nie wskazują, nie tworzysz żadnych obiektów i nic z nimi nie zrobisz przynajmniej w tym przykładzie, jeśli utworzysz jakiś obiekt klasy QWERTY i ustawisz wskaźniki na niego to będzie działało.


(ktoś tam) #5

Masz dwie opcje:

  1. Ustalasz stały rozmiar tablic i wpisujesz go do nawiasów.

  2. Ustawiasz wskaźnik na NULL lub w ogóle go nie ustawiasz i dopiero później tworzysz tablicę i przypisujesz do niej ów wskaźnik.

    //opcja 1
    #define ROZMIAR_TABLICY 123

    qwerty
    {
    char* tablica1[ROZMIAR_TABLICY];
    char* tablica2[ROZMIAR_TABLICY];
    }

    //opcja 2
    qwerty
    {
    char* tablica1 = NULL;
    char* tablica2; //w tym wypadku możesz niechcący odwołać się do przypadkowego adresu
    }

    qwerty obiekt1;
    qwerty* obiekt2;

    obiekt1.robCos();
    obiekt2->robCos();

W funkcji main obiekty wykorzystuje. Natomiast w .h trzymasz deklaracje pól i metod (private - dostęp mają tylko metody klasy i podmioty zaprzyjaźnione, protected - tak jak private, tylko że są dziedziczone, public - pełen dostęp z zewnątrz). Właściwe definicje trzymasz w .cpp (nie main.cpp).


(KamilDz) #6

To co chcesz masz już w c++. Sugeruję się zapoznać z wbudowaną klasą string:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string a;
    cin>>a;
    cout << "Wpisales " << a <<endl;
    cout << "trzecia litera to " << a[2]<<endl;  
    cin>>a;
    cout << "Wpisales " << a <<endl;
    return 0;
}

 

Jak samemu taką klase napisać:

http://stackoverflow.com/questions/16511706/simple-string-implementation-in-c

Sugeruję przeczytać:

Grębosz Symfonia c++ standard.

Rozdział o wskaźnikach i klasach. Może być nawet wersja z 2005 dostępna w sieci. Nie będę tutaj kopiował rozdziałów bo nie mam do niej praw autorskich.

 

 


(Ziomek Zemsty) #7

Dziękuję za powyższe porady, jednak w dalszym ciągu nie wiem, jak rozwiązać mój problem. Chodzi o to, w jaki sposób i gdzie (main/ plik z rozszerzeniem .h?) mam zadeklarować tablicę i jak z niej korzystać. Oczywiście tablica dynamiczna.

Treść:

/*klasa implementujaca lancuchy znakowe (bez string itp - nie korzystam z tego)


(Fizyda) #8

Do małych projektów wystarczy taka struktura plików:

MójProjekt/
    main.cpp - funckja main + includy
    inc/
        QWERTY.h
        QWERTY.cpp

W plikach nagłówkowych w 98% przypadków znajdują się tylko deklaracje funkcji, class - ich własności i metody, w cpp są to zawsze definicję funkcji oraz metod.

Czyli definicję tablicy robisz w .h, a deklarację w .cpp (o ile wymagana). Wszystko zależy od tego w jaki sposób chcesz stworzyć tablicę lub inaczej mówiąc jakiej potrzebujesz.


(KamilDz) #9

 

 

  1. Możesz pominąć podział na plik h i cpp i wszystko pisać w cpp. To rozwiąże twój problem.

  2. W klasie deklarujesz wskaźnik na tablicę char. Do konstruktora podajesz łańcuch znaków, tam wyliczasz ile on ma znaków i deklarujesz tyle pamięci na niego + jeden bajt na znak 0 za pomocą new. Później kopiujesz ten napis pod zajętą pamięć

Przykład:

 

#include <iostream>
#include <cstring>
using namespace std;

class Napis{
    public:
    char* tablicachar;
    Napis (char* a){ //to jest konstruktor
        int iloscznakow=strlen(a);
        tablicachar=new char[iloscznakow+1]; // zajmujemy wiecej o jeden na zerowy znak(tym znakiem musi się konczyc lancuch znakow w c)
        strcpy(tablicachar, a); // napis do pamieci zadeklarowanej

    };
};

int main()
{
    Napis i1("przykladowy napis");
    cout << i1.tablicachar;
}

Oczywiście musisz dopisać destruktor w którym będziesz zwalał pamięć zadeklarowaną. Jeśli będziesz chciał zmienić napis to zwalniasz pamięć i znów ją zajmujesz. 

Treść zadania które podałeś jest równoważna z:

Napisz klasę napisów (string po angielsku) ale bez użycia wbudowanej klasy string. Dlatego musisz poszukać implementacji klasy string w c++ za pomocą google. Jeden link Ci już zapodałem to kolejne:

http://www.coderscity.pl/ftopic29190.htm

http://www.mimuw.edu.pl/~szreder/poic/lab12.pdf

http://www.mimuw.edu.pl/~szreder/poic/lab13.pdf

http://www.cplusplus.com/forum/beginner/15396/

Ile masz lat i kto Ci dał te zadanie? Czy przeczytałeś jakąś książkę na temat c++?

 

 


(Ziomek Zemsty) #10

KamilDz, kończę czytać Szkołę programowania C++ Praty. Gdybym wiedział, to nie pisałbym na forum.

Odnośnie Twojego przykładu, bardzo mi to pomogło, ale chciałbym, żeby użytkownik wpisywał napis, najlepiej wielowyrazowy. I tu znowu pojawiają się schody. Wiem, że powinienem użyć cin.getline, ale gdzie tego użyć? Najlepiej w funkcji, tylko jakiej? 


(Fizyda) #11

Jestem osobą szczerą, czasami aż do bólu bo nie owijam w bawełnę.

Jeśli kończysz czytać książkę i masz tego typu problemy to jest to bardzo słaba książka, wręcz tragiczna, albo w ogólne nie myślisz nad tym co czytasz lub nie czytasz ze zrozumieniem. Książki nie znam, ale jak znam życie to obstawiam słabą książkę i troszkę to drugie.

Szczerze powiedziawszy Twoje pytania raczej wskazywały (dla mnie) na to że jesteś samoukiem który tydzień uczy się programować w C++ z “kursów” w internecie i to jeszcze takich które jak czyta osoba która rozumie temat, zaczyna wątpić w to czy do tej pory to rozumiała i jakim cudem udawało jej się pisać programy.

 

Jeśli o wprowadzanie zdań chodzi to nie ma z tym problemu, wiesz jak to zrobić, użyć możesz gdzie chcesz, najlepiej tam gdzie jest potrzeba. Na początek możesz w main. Ogólnie warto rozdzielać interfejs od logiki, czyli tam gdzie masz przetwarzanie jakiś danych ograniczasz się do ich przetwarzania, a tam gdzie coś wyświetlasz lub pobierasz od użytkownika to robisz tylko to plus ewentualnie “odpytujesz” logikę wysyłając wprowadzone przez usera rzeczy - o ile jest taka potrzeba, a potem wyświetlasz wynik - i znów jeśli jest taka potrzeba.

Ogólnie wszystko zależy od tego co robisz, nie ma jednego rozwiązania wręcz dostosowuje się rozwiązanie do problemu. Ty za mało napisałeś co chcesz zrobić więc ciężko jest coś więcej powiedzieć.


(ktoś tam) #12

 

Pliki mogą być gdziekolwiek, byleby ścieżki się zgadzały. Najlepiej jakiś podkatalog typu src lub h i cpp. 

.h - deklaracje klas, funkcji i wszystkiego innego, bez definicji (implementacji)

.cpp - implementacja wszystkiego co w .h

main - komunikacja z użytkownikiem, wykorzystanie wszelakich dóbr, które zadeklarowałeś w .h

 yyy…

wskaznikPomocniczy = lancuch1;
lancuch1 = lancuch2;
lancuch2 = wskaznikPomocniczy; 

Po prostu zwróć wskaźnik na łańcuch.

return lancuch; //char* lancuch

Pętla for.

Ogólnie potrzeba ci zrozumieć to:

http://cpp0x.pl/kursy/Kurs-C++/Konstruktor-destruktor-i-konstruktor-kopiujacy-klasy/313

http://cpp0x.pl/kursy/Kurs-C++/Tablice-zmiennych/298

http://www.cplusplus.com/reference/cstring/

https://pl.wikibooks.org/wiki/C/Napisy

http://cpp0x.pl/kursy/Kurs-C++/Wskazniki/304

 


(Ziomek Zemsty) #13

Temat do zamknięcia. Problem udało mi się rozwiązać. 

 

Fizyda, gratuluję Ci mąrdości. Sądzę, że jest godna pozazdroszczenia, wręcz unikatowa. Jednak obrażaniem inmych użytkowników forum pokazujesz tylko i wyłącznie tyle, że Twój rozwój kultury osobistej nie nadążył za intelektem. Cóż, nie można mieć wszystkiego…

 


(Fizyda) #14

■■■■ :/. W poście do którego nawiązujesz po słowach

nie napisałem, albo skasowałem żeby poprawić i nie poprawiłem, że nie chcę Cię obrazić ani nikogo innego. Fakt jest taki, że tego nie napisałem i zabrzmiało jak słowa aroganckiego i zadufanego buca który napisał coś na odwal się żeby pomóc plepsowi. Niestety nie chciałem żeby tak to zabrzmiało, ale jak teraz czytam niestety brzmi. Ponieważ czasu nie cofnę, pomimo że mógłbym edytować post i coś pozmieniać to nie lubię takiego zachowania, pozwól więc chociaż że sprostuję to co napisałem.

Napisałem tak do Ciebie ponieważ uważam, że czasami do nauki najlepszy jest kopniak w tyłek. Sam nie raz go dostawałem i wiem po sobie jak takie puszczenie w pięty potrafi “naprostować” człowieka na dobrą ścieżkę. Ja chciałem CI tymi nie ukrywajmy brutalnymi słowami zwrócić uwagę, że coś z Twoją nauką jest nie tak i prawdopodobnie korzystasz ze słabych źródeł. Miało to być motywacją byś szukał czegoś innego. Temat założyłeś bo czegoś nie zrozumiałeś, a chcesz zrozumieć i to się ceni szukasz wiedzy chcesz się nauczyć. Problem w tym że to są w C++ jednak rzeczy podstawowe, te o które pytałeś i bez nich ciężko jest porządnie wykorzystać C++, bo właśnie te trudne zawiłe rzeczy stanowią w dużej mierze o sile tego języka. Ponieważ są to rzeczy podstawowe powinny być solidnie wytłumaczone w książce którą kończysz czytać, niestety wygląda na to że jest trochę inaczej, a nie tego człowiek oczekuje płacąc za coś. Zakładam że książkę kupiłeś.

Dzięki w każdym razie za uwagę, bo sam też zauważyłem że ostatnio jestem trochę za bardzo zarozumiały, nie tylko na forum, a ja takich ludzi nie trawię i nie chciałbym stać się jednym z nich. Dlatego cenię sobie taką konstruktywną i uzasadnioną krytykę swojej osoby bo pomaga ona rozwijać się w dobrą stronę.