• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Stos z dynamiczną liczbą (nie znanego typu) elementów

Object Storage Arubacloud
0 głosów
297 wizyt
pytanie zadane 18 listopada 2022 w C i C++ przez Czarus Obywatel (1,310 p.)
edycja 18 listopada 2022 przez Czarus

Witam, walczę ostatnio z tym problemem:

#pragma once

template <class type>
class Stacker{
    struct container{
        type storage; // stored a single type value
        container* connection; // connection to next container
    };
    container* felement; // first element
    unsigned long long insize; // size of a stack
public:
    Stacker();
    ~Stacker();

    void push(const type& tobj);
    void pop();
    void clear();
    
    const type& top() const;
    const unsigned long long& size() const;
};
#include "Stacker.h"


template <class type>
Stacker<type>::Stacker(){
    ////////////////////////////////////////////////////////////////////
    this->felement = NULL;//////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////
    this->insize = 0;
}

template <class type>
Stacker<type>::~Stacker(){
    this->clear();
}

template <class type>
void Stacker<type>::push(const type& tobj){
    container* handle = new container; // new container
    handle->storage = tobj; // assign what will be stored
    handle->connection = this->felement; // point at previous container on stack
    this->felement = handle; // grab the new container 
    this->insize++;
}

template <class type>
void Stacker<type>::pop(){
    container* handle = this->felement; // grab stacks first container
    this->felement = this->felement->connection; // grab stacks second container
    delete handle; // remove unwanted first container
    this->insize--;
}

template <class type>
void Stacker<type>::clear(){
    while(this->felement != NULL)
        this->pop();
}
    
template <class type>
const type& Stacker<type>::top() const{
    if(this->felement == NULL)
        return NULL;
    else
        return this->felement->storage;
}

template <class type>
const unsigned long long& Stacker<type>::size() const{
    return this->insize;
}

Napisałem taką klasę, założenia:  Dynamiczna liczba argumentów, Nieznany Typ argumentów.

Mam 2 problemy z tym, pierwszy jest taki, że nie działa przy wywołaniu:

#include "Stacker.h"
// Kompiluję komendą: g++ main.cpp -o main Stacker.cpp
// uruchamiam: ./main
// system to Linux mint

int main(){
    Stacker<char> stack;
    printf("e stack fele: %d\n",stack.top());
    printf("size: %lld\n",stack.size());
}

Wyskakuje błąd:

/usr/bin/ld: /tmp/cc80CBJU.o: in function `main':
main.cpp:(.text+0x250): undefined reference to `Stacker<char>::Stacker()'
/usr/bin/ld: main.cpp:(.text+0x25c): undefined reference to `Stacker<char>::top() const'
/usr/bin/ld: main.cpp:(.text+0x284): undefined reference to `Stacker<char>::size() const'
/usr/bin/ld: main.cpp:(.text+0x2aa): undefined reference to `Stacker<char>::~Stacker()'
/usr/bin/ld: main.cpp:(.text+0x2d3): undefined reference to `Stacker<char>::~Stacker()'
collect2: error: ld returned 1 exit status

 

Drugim problemem jest natomiast tak jak w pliku Stacker.cpp zaznaczyłem w liniach 6,7,8 wskaźnik do elementu typu "type", chcę aby początkowo stos był pusty więc NULL wydawał się czymś odpowiednim, zwłaszcza, że używając metody "top()" zwraca ona typ "const type&" . Sam już nie wiem jak to zaimplementować, żeby mogło coś zwracać dla pustego stosu, coś co by się nie wysypywało (zakładając, że stos może być używany dla bardziej złożonych typów niż char, również dla takich gdzie nie ma konstruktora domyślnego itd).

Edit: Paradoksem jest to, że nawet gdy stos jest pusty metoda "top()" musi zwrócić referencje do obiektu, lecz nawet gdy nie zwracamy referencji ale kopię obiektu to również to by nie poszło bo tak jak wspominałem co z obiektami bez domyślnych konstruktorów, nie stworzymy takiego tylko na potrzeby zwrócenia czegokolwiek.

1
komentarz 18 listopada 2022 przez TOWaD Mądrala (6,000 p.)

na początek zobacz to,

a później może to

komentarz 18 listopada 2022 przez Czarus Obywatel (1,310 p.)

a później może to

No tutaj wszystko jest w jednym pliku (zapewne .h), ale spróbuję się tym zainspirować.

Aczkolwiek apropo tego pierwszego linku to wszędzie dookoła słyszę, że używanie:

#include "file.cpp"

jest ekwiwalentem seksu bez zabezpieczenia i nie powinno się tak robić. W dodatku z powodu, że implementacje metod i funkcji powinno się załączać jedynie w plikach .cpp a ich deklaracje w pliku .h, jestem trochę sceptyczny do pisania wszystkiego w jednym pliku, chyba że poważnie jest to jedyne rozwiązanie? Ale co wtedy z tą umowną zasadą? Pisanie całych klas w jednym pliku .h nie jest przestępstwem popełnianym tylko przez początkujące osoby lecz faktycznym zabiegiem?

trochę chaotycznie ale lepiej tego nie opiszę.

1
komentarz 18 listopada 2022 przez TOWaD Mądrala (6,000 p.)
Na to musi się koś poważniejszy wypowiedzieć.

Mi się chyba kiedyś, jakoś udało przez namespace oszukać kompilator, ale już nie pamiętam co wtedy zrobiłem.
komentarz 18 listopada 2022 przez Czarus Obywatel (1,310 p.)

No trudno już, dzięki za pomoc smiley, ten drugi link szczególnie pomocny, dobrego wieczoru.

komentarz 18 listopada 2022 przez TOWaD Mądrala (6,000 p.)

Cytując  J. Grębosza

"Definicje funkcji składowych umieszczamy także w pliku nagłówkowym, ..."

Ale to było trochę czasu temu... A.D. 2018. Przyjemnego kodowania.

1
komentarz 18 listopada 2022 przez j23 Mędrzec (194,920 p.)

Generalnie szablonów nie powinieneś rozbijać na nagłówek i plik źródłowy. Kompilator musi mieć dostępną definicję szablonu w miejscu jego konkretyzacji, dlatego szablony są z reguły w całości w nagłówkach, bo te załącza się wszędzie tam, gdzie to konieczne. Plików źródłowych nie załącza się via #include.

komentarz 18 listopada 2022 przez Czarus Obywatel (1,310 p.)

W sumie miało by to sens, np w

#include <stack>

wszystkie metody zawierają prefix template oraz ciała swoich metod w klasie.

2 odpowiedzi

+1 głos
odpowiedź 19 listopada 2022 przez mokrowski Mędrzec (155,460 p.)
wybrane 19 listopada 2022 przez Czarus
 
Najlepsza

1. Typ (a raczej makro) NULL, pochodzi z C. Z racji różnych powodów związanych z lukami w typowaniu (o tym nie ma tu miejsca pisać...), zaleca się stosowanie w C++ typu nullptr i to już od standardu C++11

2. Jeśli upierasz się aby używać wywołań printf(...) (jak najbardziej, możesz), to zrób include nagłówka cstdio. Tam jest ta funkcja.

3. Jeśli przyjmujesz że może nie być danych w wywołaniu top() (bo jak widzę zwracasz wtedy NULL), to typ zwracany to będzie type *. Generuje to masę problemów, ale jest zbliżone do Twojego pomysłu. Ja bym zwrócił dane poprzez std::optional<type>. To załatwia większość problemów.

4. Jeśli chcesz mieć ukrytą implementację szablonową (czyli nie obecną w nagłówku), możesz to jak najbardziej zrobić wymuszając budowanie konkretnej specjalizacji szablonu w pliku *.cpp. Ja osobiście w takich przypadkach, wyprowadzam ją do oddzielnego pliku *.tpp aby narzędzia IDE nie miały problemu oraz zespół który koduje rozwiązanie wiedział gdzie szukać specjalizacji. W Twoim programie, umieściłem je na końcu pliku Stacker.cpp

5. Nie będę poprawiał logiki programu. Prosiłeś o pomoc w kompilacji.

#include <cstdio>
#include "Stacker.h"
// Kompiluję komendą: g++ main.cpp -o main Stacker.cpp
// uruchamiam: ./main
// system to Linux mint
 
int main(){
    Stacker<char> stack;
    printf("e stack fele: %d\n", *(stack.top()));
    printf("size: %lld\n", stack.size());
}
#pragma once
 
template <class type>
class Stacker{
    struct container{
        type storage; // stored a single type value
        container* connection; // connection to next container
    };
    container* felement; // first element
    unsigned long long insize; // size of a stack
public:
    Stacker();
    ~Stacker();
 
    void push(const type& tobj);
    void pop();
    void clear();
     
    const type * top() const;
    const unsigned long long& size() const;
};
#include "Stacker.h"
 
template <class type>
Stacker<type>::Stacker(){
    ////////////////////////////////////////////////////////////////////
    this->felement = nullptr;//////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////
    this->insize = 0;
}
 
template <class type>
Stacker<type>::~Stacker(){
    this->clear();
}
 
template <class type>
void Stacker<type>::push(const type& tobj){
    container* handle = new container; // new container
    handle->storage = tobj; // assign what will be stored
    handle->connection = this->felement; // point at previous container on stack
    this->felement = handle; // grab the new container 
    this->insize++;
}
 
template <class type>
void Stacker<type>::pop(){
    container* handle = this->felement; // grab stacks first container
    this->felement = this->felement->connection; // grab stacks second container
    delete handle; // remove unwanted first container
    this->insize--;
}
 
template <class type>
void Stacker<type>::clear(){
    while(this->felement != nullptr)
        this->pop();
}
     
template <class type>
const type * Stacker<type>::top() const{
    if(this->felement == nullptr)
        return nullptr;
    else
        return &(this->felement->storage);
}
 
template <class type>
const unsigned long long& Stacker<type>::size() const{
    return this->insize;
}

template class Stacker<char>;

Ups... oczywiście Stacker.cpp :)

komentarz 19 listopada 2022 przez Czarus Obywatel (1,310 p.)
Przyznam, że nawet mi przez głowę nie przeszło, żeby zwrócić wskaźnik haha.

Dzięki za pomoc
komentarz 19 listopada 2022 przez mokrowski Mędrzec (155,460 p.)
No bo raczej nie powinieneś zwracać wskaźnika. Makro NULL to tak naprawdę typ void * z wartością którą można przyrównać do zera. Na nieszczęście rzutuje się to na dowolny typ. Podkreślam jeszcze raz. Powinieneś w takich miejscach zwracać std::optional a nie błędogenne wskaźniki. Ale nie miałem Ci przerabiać programu tylko pomóc w kompilacji.
+1 głos
odpowiedź 18 listopada 2022 przez j23 Mędrzec (194,920 p.)

Sam już nie wiem jak to zaimplementować, żeby mogło coś zwracać dla pustego stosu, coś co by się nie wysypywało

Opcje masz w sumie dwie:

  • top zwraca wartość typu std::optional.
  • top rzuca wyjątek (wtedy warto dodać metodę isEmpty).
komentarz 18 listopada 2022 przez Czarus Obywatel (1,310 p.)

W sumie nawet nie wiedziałem że coś takiego jak std::optional istnieje, muszę bardziej poznać uroki C++17 cool

Dzięki

Podobne pytania

0 głosów
1 odpowiedź 187 wizyt
pytanie zadane 3 sierpnia 2015 w C i C++ przez Dash Nałogowiec (29,650 p.)
0 głosów
1 odpowiedź 199 wizyt
0 głosów
4 odpowiedzi 827 wizyt
pytanie zadane 16 sierpnia 2016 w C i C++ przez Munvik Dyskutant (9,350 p.)

92,576 zapytań

141,426 odpowiedzi

319,652 komentarzy

61,961 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...