Projekt - kreator baz danych

0

Witam,

Zabrałem się za dosyć ambitny, jak na mój poziom oczywiście, projekt. A mianowicie wpadłem na pomysł stworzenia w języku C++ prostego kreatora baz danych. Z założenia użytkownik miał mieć możliwość definiowania ile i jakiego typu kolumn będzie miała baza danych, którą chce zdefiniować. Przykładowo użytkownik chce stworzyć bazę danych, która będzie zawierała imię, nazwisko, datę urodzin i rozmiar buta, a potem, za pomocą tego samego programu stworzyć bazę, której kolumnami będą same inty. Niestety, nie udało mi się ominąć problemu z faktem, że w liście dwukierunkowej muszę mieć dokładnie określone, po jakich typach wyznacznik przechodzi... przynajmniej tak ja rozumiem mój problem. Poniżej zamieszczam kod, mam nadzieję, że ktoś ma pomysł jak to ominąć.

Baza.h:

#pragma once
#include "Kolumna.h"

template <typename T,typename D>
class Baza
{

public:

	int l_kolumn;
	int l_wierszy;

	Kolumna<T>* poczatek;
	Kolumna<D>* koniec;

	string nazwa;

	Baza();
	~Baza() {};
	friend Baza* utworz_nowa_baze();

};

template <typename T>
Baza::Baza():poczatek(0),koniec(0),nazwa("Nowa Baza"),l_kolumn(0),l_wierszy(0)
{}

template <typename T>
Baza* utworz_nowa_baze()
{
	Baza nowa_b;

	cout << "Jaka nazwe ma miec Twoja baza?" << endl; //Okno wprawadzenia nazwy bazy
	cin >> nowa_b.nazwa;

	cout << "Ile ma miec kolumn? " << endl; 
	cin >> l_kolumn;


	//tworzenie kolumn
	for(int i=0;i<l_kolumn;i++)
	{
		int b;
		cout << "Jakiego typu ma to być kolumna? " << endl;
		cin >> b;
		// Sposób wyboru
		// case 'b' : Kolumna<int>* wsk=new Kolumna<int>();
		switch(b)
		{
			case 1:

				Kolumna<int>* wsk=new Kolumna<int>();
				break;

			case 2:

				Kolumna<float>* wsk=new Kolumna<float>();
				break;

			case 3:

				Kolumna<string>* wsk=new Kolumna<string>();
				break;
		}
		Kolumna<T>* wsk=new Kolumna<T>();
		cout << "Jaka nazwe mma miec kolumna nr " << i+1 << " ?" << endl;
		cin >> wsk->nazwa;
		if(nowa_b.poczatek == NULL)
		{
			nowa_b.poczatek = wsk;
			nowa_b.koniec = wsk;
		}

		else 
		{ 
			nowa_b.koniec->nastepny=wsk;
			wsk->poprzedni=nowa_b.koniec;
			nowa_b.koniec = wsk;
		}
	}
}

Kolumna.h: (tu jest chyba największy problem)

#include "Pole.h"



template <typename T,typename C,typename D>;


class Kolumna
{

public:
	
	static int l_wierszy;
	string nazwa;

	Pole<T>* poczatek;
	Pole<T>* koniec;

	Kolumna<D>* poprzedni_k;
	Kolumna<C>* nastepny_k;

	Kolumna();
	~Kolumna();

	bool isempty();
	void add(T item);
	void removeFirst();
	void removeChosen(int x);
	void print();

};


template <typename T,typename C,typename D>
Kolumna<T>::Kolumna():poczatek(NULL),koniec(NULL)
{}
template <typename T,typename C,typename D>
Kolumna<T>::~Kolumna()
{
	while(poczatek!=NULL)
		this->removeFirst();
}

template <typename T,typename C,typename D>
bool Kolumna<T>::isempty()
{
	return poczatek == NULL;
}

template <typename T,typename C,typename D>
void Kolumna<T>::add(T item) // dodawanie sortowane
{
	Pole<T>* wsk=new Pole<T>(item);
	
	if(poczatek == NULL)
	{
		poczatek = wsk;
		koniec = wsk;
	}

	else 
	{ 
		Pole<T>* temp=poczatek;

		while (temp != NULL)
			temp=temp->nastepny;

		wsk->poprzedni=temp;
		temp->nastepny=wsk;
	}

}

template <typename T,typename C,typename D>
void Kolumna<T>::removeFirst()
{
	if(!(this->isempty()))
	{
		Pole<T>* wsk=poczatek;
		poczatek=poczatek->nastepny;
		poczatek->poprzedni=NULL;
		delete wsk;
	}
}

template <typename T,typename C,typename D>
void Kolumna<T>::removeChosen(int x)
{
	if(this->isempty())
		return;

		Pole<T>* wsk=poczatek;

		while(wsk->numer!=x)
			wsk=wsk->nastepny;

		wsk->poprzedni->nastepny=wsk->nastepny;
		wsk->nastepny->poprzedni=wsk->poprzedni;

		Pole<T>* zmiana_numeracji=wsk->nastepny;
		delete wsk;
		while (zmiana_numeracji!=NULL)
			zmiana_numeracji->numer-=1;

	
}

template <typename T> // do zmiany
void Kolumna<T>::print()
{
	Pole<T>* wsk=head;
	while(wsk!=NULL)
	{
		cout << *wsk;
		cout << endl;
		wsk=wsk->nastepny;
	}
}

Pole.h:

#pragma once
#include <iostream>
using namespace std;

template <typename T>
class Pole
{
public:
	Pole* nastepny;
	Pole* poprzedni;
	int numer;

	T dane;

	Pole();
	Pole(T nowe);
	~Pole()  {};

};


template <typename T>
Pole<T>::Pole():nastepny(0), poprzedni(0)
{
}

template <typename T>
Pole<T>::Pole(T nowe): dane(nowe), nastepny(0), poprzedni(0)
{
}
0
Atime napisał(a):

Niestety, nie udało mi się ominąć problemu z faktem, że w liście dwukierunkowej muszę mieć dokładnie określone, po jakich typach wyznacznik przechodzi...

Nie rozumiem o co tutaj Ci chodzi. Napisz swoją myśl inaczej, spróbuj trochę jaśniej. Tak czy inaczej odniosę się do kodu ze swoimi spostrzeżeniami.

Atime napisał(a):
template <typename T,typename D>
class Baza
{
 
public:
 
        int l_kolumn;
        int l_wierszy;
 
        Kolumna<T>* poczatek;
        Kolumna<D>* koniec;
 
        string nazwa;
 
        Baza();
        ~Baza() {};
        friend Baza* utworz_nowa_baze();
 
};

Sądzę że niepotrzebnie brniesz w tworzenie swojej listy. Skorzystaj z stl'owego wektora lub listy. Po co zmienna na temat wierszy? Niepotrzebne, bo w programie przecież masz tworzyć strukturę a nie zawartość bazy. Nie widzę potrzeby stosowania licznika dla kolumn - tą informację wydobędziesz z listy, wektora poprzez zliczenie elementów.

Tak poza tym klasa Baza powinna zawierać kolekcję obiektów typu Tabela, a dopiero Tabela powinna zawierać Kolumny.

Każdy silnik bazodanowy ma swój dialekt - w mniejszym lub większym stopniu(pierwsze z brzegu - nazewnictwo typów). Taką informację przechowywałbym w enum'ach. Zamiast robić jakieś ify itp. zrób przypisanie enuma, który precyzuje typ kolumny. Dzięki temu zrobisz abstrakcję, na której klasa konkretna przeznaczona do generowania komend SQL będzie używała składni danego silnika bazodanowego(wykorzystasz np. wzorzec strategia).

Nie podoba mi się, że używasz w plikach *.h tylko pragma once - stosuj jednak ifndef - będzie bezpieczniej.

0

Zabrałem się za dosyć ambitny, jak na mój poziom oczywiście, projekt. (...) Poniżej zamieszczam kod, mam nadzieję, że ktoś ma pomysł jak to ominąć.

Zanim zaczniesz realizować takie projekty uzupełnij swoją wiedzę na temat baz danych i szablonów, bo z prezentowanego tu kodu to nie masz o tym zielonego pojęcia. A braku wiedzy ominąć się nie da.

Przykład z brzegu:
Kolumna<T>* poczatek; //to ma się skompilować! ---> template <typename T,typename C,typename D> class Kolumna
albo to:
template <typename T>
Baza::Baza():poczatek(0),koniec(0),nazwa("Nowa Baza"),l_kolumn(0),l_wierszy(0){}// ---> template <typename T,typename D> class Baza

O projekcie bazy pisał Hostel. Ale najpierw trzeba potrafić to zaprogramować.

0

IMHO projekt zbyt ambitny jak na możliwości autora...
Ale widziałem już gorsze zadania domowe do zrobienia...

Co do pytania to musisz mieć raczej pole które potrafi dynamicznie (a nie w czasie kompilacji) mieć przydzielony typ danych.
W związku z tym musisz zamienić:

template <typename T>
class Pole {
 T dane;
};

na coś bardziej dynamicznego:

boost::any to coś podobnego do tego co masz, tylko że sprytnie zapakowane. Dlatego można zmieniać wartość pola na dowolny typ.
Tu jest poradnik jak użyć:
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=282

Stosowanie Boost wymaga instalacji tego pakietu - nie każdemu się udaje (ja nie miałem z tym problemu).

Do pobrania tutaj:
http://sourceforge.net/projects/boost/files/boost/1.51.0/

Strona domowa:
http://www.boost.org/

1 użytkowników online, w tym zalogowanych: 0, gości: 1