Problem z template.

Problem z template.
mistrzuniu1
  • Rejestracja:około 8 lat
  • Ostatnio:prawie 6 lat
  • Postów:51
0

Pierwszy raz tworzę klasę z wykorzystaniem template. Jest to klasa wektor. Mogły mi ktoś wskazać co robię zle?

wektor.h

Kopiuj
#pragma once
#include <string>
template < class T >
class Wektor
{
	T *s; int rozmiar;
public:
	Wektor();
	void push(T x);
	int size();
	~Wektor();
};

wektor.cpp

Kopiuj

#include "Wektor.h"


template <class T>
Wektor<T>::Wektor():s(nullptr), rozmiar(0)
{}

template<class T>
void Wektor<T>::push(T x)
{
	delete s;
	s = new T[rozmiar + 1];
	rozmiar++;
}
template<class T>
int Wektor<T>::size()
{
	return rozmiar;
}
template<class T>
Wektor<T>::~Wektor()
{
	delete s;
}


main.cpp

Kopiuj
Wektor<int> x;
return 0;

Bład który wyskakuje:
Error LNK2019 unresolved external symbol "public: __thiscall Wektor<int>::~Wektor<int>(void)" (??1?$Wektor@H@@QAE@XZ) referenced in function _main

grzesiek51114
grzesiek51114
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:2442
2

Wrzuć wszystko do pliku *.h i zadziała. Template'y tak już mają.
Poza tym kod zawiera błędy. Kompiluje się, kiedy wrzucę wszystko do headera, ale jest nieprawidłowy, ponieważ robisz tam masę dziwnych rzeczy i to nawet nie dotyczy samych szablonów.

  • Robisz delete nie sprawdzając wskaźnika;
  • Przekazujesz generyczny obiekt przez wartość zamiast refrencją;
  • W pushu tracisz kompletnie dane, bo z przekazanym obiektem niczego nie robisz.
  • Każdy Twój push utworzy nową tablicę więc za każdym razem utracisz stare dane, bo w ogóle nie ma obsługi starych wartości.
  • To się wszystko wysypie ;)
edytowany 8x, ostatnio: grzesiek51114
mwl4
Robisz delete nie sprawdzając wskaźnika; - In all cases, if ptr is a null pointer, the standard library deallocation functions do nothing (http://en.cppreference.com/w/cpp/memory/new/operator_delete)
mistrzuniu1
  • Rejestracja:około 8 lat
  • Ostatnio:prawie 6 lat
  • Postów:51
0
grzesiek51114 napisał(a):

Wrzuć wszystko do pliku *.h i zadziała. Template'y tak już mają.
Poza tym kod zawiera błędy. Kompiluje się, kiedy wrzucę wszystko do headera, ale jest nieprawidłowy, ponieważ robisz tam masę dziwnych rzeczy i to nawet nie dotyczy samych szablonów.

  • Robisz delete nie sprawdzając wskaźnika;
  • Przekazujesz generyczny obiekt przez wartość zamiast refrencją;
  • W pushu tracisz kompletnie dane, bo z przekazanym obiektem niczego nie robisz.
  • Każdy Twój push utworzy nową tablicę więc za każdym razem utracisz stare dane, bo w ogóle nie ma obsługi starych wartości.
  • To się wszystko wysypie ;)

Oczywiscie masz racje, chciałem żeby te template zadziałały po prostu.

mistrzuniu1
  • Rejestracja:około 8 lat
  • Ostatnio:prawie 6 lat
  • Postów:51
0

Jak powinien wyglądać destruktor, gdy jednym z typ jest typ string?
string jest sam w sobie tablica dynamiczna. Usunie się ona sama, czy muszę to jakoś obsługiwać?

Kopiuj

~Wektor()
	{
		if(s!= nullptr)
		delete s;
	}
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:około 6 godzin
  • Lokalizacja:Szczecin
4

Jeśli już musisz używać new/delete (patrz: tutaj), to koniecznie pamiętaj, że każde wywołanie new musi mieć odpowiadające wywołanie delete, a new[] - delete[]. Mieszanie ich to UB. Ponadto musisz respektować regułę 3/5/0.


mistrzuniu1
  • Rejestracja:około 8 lat
  • Ostatnio:prawie 6 lat
  • Postów:51
0
kq napisał(a):

Jeśli już musisz używać new/delete (patrz: tutaj), to koniecznie pamiętaj, że każde wywołanie new musi mieć odpowiadające wywołanie delete, a new[] - delete[]. Mieszanie ich to UB. Ponadto musisz respektować regułę 3/5/0.

Tworzę projekt na studia, nie wolno nam korzystać z STL.
Powiedziałbym, że mój problem tkwi w tym, że chciałbym używać jako "szablonowego" typu stringa. Jak powinienem to obsłużyć w destruktorze, mając na uwadze to, że nie będzie to tylko string.
Jest jakaś funkcja, która pomoże mi sprawdzić typ wskaźnika?
Jeśli uda mi się to zrobić, to jak powinien zachowywać się destruktor(bo przy powyższej implemetacji mam wyciek pamieci)?

twonek
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 2 lata
  • Postów:2500
1

string umie sprzątać po sobie jeśli trzymasz obiekt, a nie wskaźnik. Więc pytanie raczej brzmi "dlaczego masz T* s a nie T s?"
Edit: ok implementujesz tablicę dynamiczną.

edytowany 1x, ostatnio: twonek
grzesiek51114
grzesiek51114
Dałem plusa z auta i też nie zauważyłem tego co skreśliłeś ;) ...a raczej o tym zapomniałem.
grzesiek51114
grzesiek51114
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:2442
1

Powiedziałbym, że mój problem tkwi w tym, że chciałbym używać jako "szablonowego" typu stringa.

@mistrzuniu1: No ale skoro tworzysz szablon to typem będzie generyk T, bo tak w sumie najrozsądniej zrobić. Tworzysz kontener na dane abstrakcyjnego typu T, który może być czymkolwiek: strukturą, klasą, prymitywem etc... IMHO string jest Ci do niczego nie potrzebny.

Jeżeli natomiast chcesz zrobić kontener i chcesz go samemu zaimplementować w szablonie to może warto posłużyć się listą, którą np. samemu napiszesz? https://4programmers.net/Forum/C_i_C++/288340-lista_jednokierunkowa_to_do_list_pomoc_dla_nooba?p=1352973#id1352973

No, bo kontener to kontener na cokolwiek chcę: taka w sumie jest idea szablonów.

edytowany 1x, ostatnio: grzesiek51114
MO
  • Rejestracja:około 10 lat
  • Ostatnio:3 dni
  • Lokalizacja:Tam gdzie jest (centy)metro...
1

wektor.h:

Kopiuj
#pragma once

template < class T >
class Wektor
{
    T *s; int rozmiar;
public:
    Wektor();
    void push(T x);
    size_t size() const;
    ~Wektor();
};

wektor.cpp:

Kopiuj
#include "wektor.h"
 
template <class T>
Wektor<T>::Wektor(): s{nullptr}, rozmiar{}
{}
 
template<class T>
void Wektor<T>::push(T x)
{
    delete s;
    s = new T[rozmiar + 1];
    rozmiar++;
}

template<class T>
size_t Wektor<T>::size() const
{
    return rozmiar;
}

template<class T>
Wektor<T>::~Wektor()
{
    delete s;
}

// To wydaje się być najbardziej sensowne rozwiązanie Twojego problemu.
template class Wektor<int>;

main.cpp:

Kopiuj
#include <cstdlib>
#include "wektor.h"

int main()
{
    Wektor<int> x;
    
    return EXIT_SUCCESS;
}

Każdy problem w informatyce można rozwiązać, dodając kolejny poziom pośredniości,z wyjątkiem problemu zbyt dużej liczby warstw pośredniości — David J. Wheeler
stryku
  • Rejestracja:ponad 11 lat
  • Ostatnio:prawie 2 lata
  • Postów:607
4
Mokrowski napisał(a):

wektor.cpp:

Kopiuj
// To wydaje się być najbardziej sensowne rozwiązanie Twojego problemu.
template class Wektor<int>;

Nie zgodzę się. To zabija całą generyczność Wektora. Najlepsze będzie przeniesienie implementacji do hpp IMHO (:

edytowany 1x, ostatnio: stryku
MO
Nie zgadzam się. Innym równoważnym rozwiązaniem jest #include pliku wektor-imp.tpp lub wektor-impl.cpp z tym wpisem. W pliku *.hpp nie powinieneś eksponować skonkretyzowanych szablonów a do tego się to sprowadza. Przecież *.hpp ma być dostarczony klientom a *.cpp jest plikiem implementacji. Ale .. spokojnie.. akurat ten element wzbudza kontrowersje. Np. w Qt dochodzi wręcz do #include plik.cpp :-/ A zresztą... do poczytania https://isocpp.org/wiki/faq/templates#separate-template-class-defn-from-decl

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.