•
•
•
•
What is DaniWeb IT Discussion Community?
You're currently browsing the C++ section within the Software Development category of DaniWeb, a massive community of 375,170 software developers, web developers, Internet marketers, and tech gurus who are all enthusiastic about making contacts, networking, and learning from each other. In fact, there are 2,237 IT professionals currently interacting right now! Registration is free, only takes a minute and lets you enjoy all of the interactive features of the site.
Please support our C++ advertiser:
Views: 946 | Replies: 9
![]() |
•
•
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation:
Rep Power: 2
Solved Threads: 0
Hi everybody:
I'm trying to do the practices on the top of forum. I started from
beginner's level. This time I will show you my solution to implement
a simple database in C++. If you could give some advice on the
following aspects or other, it would be so nice of you
1. base datastructure. I used a 2D array to store a number of
pointers to string data. However, it's not easy to resize. I find it's not
easy to add a column.
2. design pattern. My solution really looks like a hardcoded solution
only to this question. How to find a better a design pattern than could
be reused easily?
3.algothirms.
4. multithreads and process protection
5....
I hope my post is not too long to make you boring.
my codes:
You have to create a file called "d:/database.txt" on your disk, the contens look like this"name Gender old Rock male 100".
Thankyou very much for watching.
I'm trying to do the practices on the top of forum. I started from
beginner's level. This time I will show you my solution to implement
a simple database in C++. If you could give some advice on the
following aspects or other, it would be so nice of you

1. base datastructure. I used a 2D array to store a number of
pointers to string data. However, it's not easy to resize. I find it's not
easy to add a column.
2. design pattern. My solution really looks like a hardcoded solution
only to this question. How to find a better a design pattern than could
be reused easily?
3.algothirms.
4. multithreads and process protection
5....
I hope my post is not too long to make you boring.
my codes:
C++ Syntax (Toggle Plain Text)
#include "stdafx.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "io.h" const char *filename = "d:/database.txt"; const int ROWS = 10; const int COLOUMS = 3; class Data { public: Data() : m_nTotalColumns(COLOUMS), m_nTotalRows(ROWS), m_nCurCol(0), m_nCurRow(0), m_ppcData(NULL), m_fp(NULL) { } virtual ~Data() {} //create a dynamic 2 dimension char * array which points to a single data string bool Init() { m_ppcData = (char **)new char *[ROWS][COLOUMS]; if(NULL == m_ppcData) { return false; } memset((void *)m_ppcData, NULL, ROWS * COLOUMS * sizeof(char *)); return ReadFile(); } //insert a record with all values of fields void Insert(const char *name, const char *Gender, const char *old) { if (NULL == name || NULL == Gender || NULL == old) { return; } //array is full if(++m_nCurRow >= m_nTotalRows) { return; } StrCpy(m_ppcData[m_nCurRow * m_nTotalColumns], name); StrCpy(m_ppcData[m_nCurRow * m_nTotalColumns + 1], Gender); StrCpy(m_ppcData[m_nCurRow * m_nTotalColumns + 2], old); } //updata a special data void Update(const unsigned int row, const unsigned int column, const char *data) { if(row > m_nCurRow || column >= m_nTotalColumns || NULL == data) { return; } StrCpy(m_ppcData[row * m_nTotalColumns + column], data); } //select a special data char *Select(const unsigned int row, const unsigned int column) { if(row > m_nCurRow || column >= m_nTotalColumns) { return NULL; } return m_ppcData[row * m_nTotalColumns + column]; } //delete a row void DeleteRecord(const unsigned int row) { if(row > m_nCurRow) { return; } unsigned int i; unsigned int j; for(i = 0; i < m_nTotalColumns; i++) { delete [] m_ppcData[row * m_nTotalColumns + i]; m_ppcData[row * m_nTotalColumns + i] = NULL; } //if the deleted row is not the last, i will fflush the data to the top if(row < m_nCurRow) { for(i = row; i < m_nCurRow; i++) { for(j = 0; j < m_nTotalColumns; j++) { m_ppcData[i * m_nTotalColumns + j] = m_ppcData[(i + 1) * m_nTotalColumns + j]; } } } memset((void *)(m_ppcData + m_nCurRow * m_nTotalColumns), NULL, COLOUMS * sizeof(char *)); m_nCurRow--; } void display() { printf("**************\n"); for(int i = 0; i < ROWS * COLOUMS; ++i) { if (NULL != m_ppcData && NULL != m_ppcData[i]) { printf("%s\n ", m_ppcData[i]); } } } void Save() { WriteFile(); } protected: const char* StrCpy(char *&dest, const char *src) { if(NULL == src) { return NULL; } //avoid copy self if(src == dest) { return dest; } //release original memory if(NULL != dest) { delete [ ]dest; dest = NULL; } int len = strlen(src); dest = new char[len + 1]; if(NULL == dest) { return NULL; } return strcpy(dest, src); } //write data into file bool WriteFile() { m_fp = fopen(filename, "w+"); if(NULL == m_fp) { return false; } for(int i = 0; i < ROWS * COLOUMS; ++i) { if (NULL != m_ppcData && NULL != m_ppcData[i]) { fputs(m_ppcData[i], m_fp); fputs(" ", m_fp); } } fclose(m_fp); return true; } //Read data From file //load all the data from the disk, do I need to do so? bool ReadFile() { m_fp = fopen(filename, "r+"); if(NULL == m_fp) { return false; } long length = _filelength(_fileno(m_fp)) + 1; char *tmp = new char[length]; if(NULL == tmp) { return false; } fgets(tmp, length, m_fp); int i = 0; char *pre = tmp; char *t = tmp; unsigned int pos; bool bIsEOF = false; for(i = 0, pos = 0; (i < length) && (pos < m_nTotalRows * m_nTotalColumns); ++i) { if(*t == '\0' || *t == ' ') { //if we come to the end of the data, break if(*t == '\0') { StrCpy(m_ppcData[pos], pre); break; } else { *t = '\0'; StrCpy(m_ppcData[pos], pre); //deal with multi spaces while (*++t == ' ') { } pre = t; pos++; } } &nref="http://bbs.fdc.com.cn/boardlist.asp?boardid=376" target=_blank>清江花园 [url="http://bbs.fdc.com.cn/boardlist.asp?boardid=277"]秦园居[/url] </DIV> <DIV class=tabcontent id=tcontent5> p; delete [] tmp; fclose(m_fp); return true; } private: const unsigned int m_nTotalColumns; const unsigned int m_nTotalRows; unsigned int m_nCurCol; unsigned int m_nCurRow; FILE *m_fp; char **m_ppcData; }; int main(int argc, char* argv[]) { Data d; if (d.Init()) { d.Update(2, 2, "100"); d.Insert("Rock", "male", "24"); d.display(); char * p = d.Select(3, 2); printf("p = %s\n", p); d.DeleteRecord(1); d.display(); d.Save(); } return 0; }
You have to create a file called "d:/database.txt" on your disk, the contens look like this"name Gender old Rock male 100".
Thankyou very much for watching.
Rock Mu
I will keep walking!
Shanghai, China
I will keep walking!
Shanghai, China
•
•
Join Date: Aug 2005
Location: near St Louis, Missouri, USA
Posts: 10,199
Reputation:
Rep Power: 34
Solved Threads: 824
don't use c style file i/o and strings in c++ programs. Yes its valid to do that but c++ file i/o and c++ string class are generally easier to use and makes your program less buggy.
c++ vectors and lists are the containers that will make it almost trivel to resize. You can create a 2d vector (rows and columns) by using a vector of vectors, something like this.
c++ vectors and lists are the containers that will make it almost trivel to resize. You can create a 2d vector (rows and columns) by using a vector of vectors, something like this.
typedef vector<string> COLUMNS; vector<COLUMS> rows;
Last edited by Ancient Dragon : Jun 11th, 2007 at 7:14 am.
'Politics' is made up of two words, 'poli,' which is Greek for 'many,' and 'tics,' which are blood-sucking insects.
- Gore Vidal
Being ignorant is not so much a shame as being unwilling to learn. - Benjamin Franklin
- Gore Vidal
Being ignorant is not so much a shame as being unwilling to learn. - Benjamin Franklin
•
•
Join Date: Feb 2007
Location: Bangalore, India
Posts: 535
Reputation:
Rep Power: 4
Solved Threads: 50
A few comments:
1. Given it's C++ problem, try using C++ containers (instead of array) e.g. vector/list/map.
2. You don't really need to force use of a design. Not yet at least.
3. Comment on the design: Class Data should not be responsible for insertion/deletion/.. Look at it purely from english pov, Data itself shouldn't have this functionality. If you look at your problem stmt, you'll see that you're missing a major class (named Database). Database should be the one who has this functionality.
1. Given it's C++ problem, try using C++ containers (instead of array) e.g. vector/list/map.
2. You don't really need to force use of a design. Not yet at least.
3. Comment on the design: Class Data should not be responsible for insertion/deletion/.. Look at it purely from english pov, Data itself shouldn't have this functionality. If you look at your problem stmt, you'll see that you're missing a major class (named Database). Database should be the one who has this functionality.
Are you Agile.. ?
•
•
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation:
Rep Power: 2
Solved Threads: 0
Thanks for all of you.
I tried to give a more c++ solution in the following. Acturally, it's the
first time I use some STL and exception mechanism in my codes.
So it would so nice of you to give me some advice on my codes
Ps: the definitions of list, vector.. in headers LIST, VECTOR...
are really difficult to understand. Why does that coder write that
complicated codes? To avoid the codes being understood by others?
I tried to give a more c++ solution in the following. Acturally, it's the
first time I use some STL and exception mechanism in my codes.
So it would so nice of you to give me some advice on my codes

C++ Syntax (Toggle Plain Text)
#include "stdafx.h" #pragma warning(disable:4786) #include <vector> #include <iostream> #include <fstream> #include <string> #include <algorithm> #include <iterator> #define RECORDMAXLENGstringH 100 using namespace std; //template <class string> class Database { public: Database() { } virtual ~Database() { } Database(const Database &d) { m_vRow = d.m_vRow; } const Database& operator=(const Database& d) { if (this != &d) { this->m_vRow = d.m_vRow; } return *this; } const string& Select(const int r, const int c) { if(r >= m_vRow.size()) { throw r; } if(c >= m_vRow[0].size()) throw c; cout<<m_vRow[r][c]<<"\n"; return m_vRow[r][c]; } bool Update(const int r, const int c, const string & Data) { if(r >= m_vRow.size()) return false; if(c >= m_vRow[0].size()) return false; m_vRow[r][c] = Data; } bool DeleteRecord(const int r) { if(r >= m_vRow.size()) throw r; int i = 0; ROW::iterator itr = m_vRow.begin(); while (i++ < r) { itr++; } m_vRow.erase(itr); return true; } void AddRecord(const string & name, const string& gender, const string &old) { COLUMN column; column.push_back(name); column.push_back(gender); column.push_back(old); m_vRow.push_back(column); } bool Init() { fstream fs("d:/database.txt", ios::in); string line; COLUMN column; //get a line from file and analyse the words in it while (getline(fs, line, '\n')) { string::size_type pos = 0; string::size_type pre = 0; string record; //when i find a space, it means I find a whole word while ((pos = line.find(' ', pos)) != string::npos) { record = line.substr(pre, pos - pre); pre = ++pos; //ignore multispace if (record != "") { column.push_back(record); } } //deal with the last word end with '\0' record = line.substr(pre, string::npos); column.push_back(record); m_vRow.push_back(column); column.clear(); } fs.close(); return false; } bool Save() { fstream fs("d:/database.txt", ios::trunc | ios::out); COLUMN::iterator itc; ROW::iterator itr; for(itr = m_vRow.begin(); itr != m_vRow.end(); itr++) { for(itc = (*itr).begin(); itc != (*itr).end(); itc++) { fs<<(*itc)<<' '; } fs<<'\n'; } fs.close(); return false; } void display() { COLUMN::iterator itc; ROW::iterator itr; for(itr = m_vRow.begin(); itr != m_vRow.end(); itr++) { for(itc = (*itr).begin(); itc != (*itr).end(); itc++) { cout<<*itc<<' '; } cout<<'\n'; } } private: typedef vector<string> COLUMN; typedef vector<COLUMN> ROW; ROW m_vRow; //const static char* m_sFilename; }; //template <class string> //string //const char* Database::m_sFilename = "d:/database.txt"; int main(int argc, char* argv[]) { Database database; try { database.Init(); database.display(); database.AddRecord("Chirly", "female", "18"); database.display(); database.Select(3, 1); database.DeleteRecord(5); database.display(); database.Save(); } catch (int i) { cout<<i<<"too large\n"; } catch(bad_alloc &) { cout<<"bad alloc\n"; } return 0; }
Ps: the definitions of list, vector.. in headers LIST, VECTOR...
are really difficult to understand. Why does that coder write that
complicated codes? To avoid the codes being understood by others?
Rock Mu
I will keep walking!
Shanghai, China
I will keep walking!
Shanghai, China
>So it would so nice of you to give me some advice on my codes
Granted, all I did was skim over it, but your use of the standard library and exceptions looks okay.
>Why does that coder write that complicated codes?
Well, it looks like you're using Visual Studio, and the standard library code for Visual Studio is notoriously obtuse. But there's always going to be some measure of complexity in code that does so much with templates.
Granted, all I did was skim over it, but your use of the standard library and exceptions looks okay.
>Why does that coder write that complicated codes?
Well, it looks like you're using Visual Studio, and the standard library code for Visual Studio is notoriously obtuse. But there's always going to be some measure of complexity in code that does so much with templates.
Member of: Beautiful Code Club.
•
•
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation:
Rep Power: 2
Solved Threads: 0

c++ Syntax (Toggle Plain Text)
const string& Select(const int r, const int c) { if(r >= m_vRow.size()) { throw r; } if(c >= m_vRow[0].size()) throw c; cout<<m_vRow[r][c]<<"\n"; return m_vRow[r][c]; }
In fact, in this function, I tried to return something instead of
throw something. But it's not easy to return something, I have to
return a string object if r >= m_vRow.size() is true. What to return?
I have no idea.
Last edited by kinggarden : Jun 13th, 2007 at 10:15 am.
Rock Mu
I will keep walking!
Shanghai, China
I will keep walking!
Shanghai, China
>What to return?
An exception is the better option in this case. But if you want to return something, you need to pick a string that couldn't possibly be in the database. That's difficult to do, so you might do well to add a level of indirection by using an object for each cell rather than a string. Then you can easily give it a null value and any other cellish information that isn't easily derived from the value:
An exception is the better option in this case. But if you want to return something, you need to pick a string that couldn't possibly be in the database. That's difficult to do, so you might do well to add a level of indirection by using an object for each cell rather than a string. Then you can easily give it a null value and any other cellish information that isn't easily derived from the value:
class Cell {
bool is_null;
std::string value;
public:
Cell ( const std::string& init = "" )
: is_null ( init.size() == 0 ), value ( init )
{}
public:
const std::string& GetValue() const
{
return value;
}
void SetValue ( const std::string& s )
{
value = s;
is_null = false;
}
bool IsNull() const
{
return is_null;
}
}; Member of: Beautiful Code Club.
•
•
Join Date: Aug 2005
Location: near St Louis, Missouri, USA
Posts: 10,199
Reputation:
Rep Power: 34
Solved Threads: 824
•
•
•
•
Ps: the definitions of list, vector.. in headers LIST, VECTOR...
are really difficult to understand. Why does that coder write that
complicated codes? To avoid the codes being understood by others?
I agree -- and I don't bother any more trying to read those header files. You can find better explanations for the stl classes with google. And you can find some example programs with google too. Also check out the c++ code snippets here at DaniWeb.
'Politics' is made up of two words, 'poli,' which is Greek for 'many,' and 'tics,' which are blood-sucking insects.
- Gore Vidal
Being ignorant is not so much a shame as being unwilling to learn. - Benjamin Franklin
- Gore Vidal
Being ignorant is not so much a shame as being unwilling to learn. - Benjamin Franklin
•
•
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation:
Rep Power: 2
Solved Threads: 0
•
•
•
•
>What to return?
An exception is the better option in this case. But if you want to return something, you need to pick a string that couldn't possibly be in the database. That's difficult to do, so you might do well to add a level of indirection by using an object for each cell rather than a string. Then you can easily give it a null value and any other cellish information that isn't easily derived from the value:
class Cell { bool is_null; std::string value; public: Cell ( const std::string& init = "" ) : is_null ( init.size() == 0 ), value ( init ) {} public: const std::string& GetValue() const { return value; } void SetValue ( const std::string& s ) { value = s; is_null = false; } bool IsNull() const { return is_null; } };
Thank u very much.
But I still have a question, if I write something like this
c++ Syntax (Toggle Plain Text)
const Cell & Test() { return NULL; //I just want to tell something is wrong }
Of course, I defined a copy constructor in Class Cell like this
C++ Syntax (Toggle Plain Text)
Cell(const int i) { if(0 == i) is_null =true; }
But it doesn't work well, there is a waring "returning address of local
variable or temporary". I know that, a temp object is generated in
Test and the reference of that object is returned.
In fact, i think Test() returns a reference of something, it must be
a long life object(i mean not temp or local). But at that time, i don't
have such a object, how to return?
Thank you
Rock Mu
I will keep walking!
Shanghai, China
I will keep walking!
Shanghai, China
>const Cell & Test() {
> return NULL; //I just want to tell something is wrong
There's no such thing as a null reference in C++. If all you want to do is return a single error state, you can follow the same pattern as the iostream library:
This returns a null pointer if the cell is null, or a pointer to the object if it's not. If you aren't comfortable working with overloaded type conversions, you can do the same thing with Test:
> return NULL; //I just want to tell something is wrong
There's no such thing as a null reference in C++. If all you want to do is return a single error state, you can follow the same pattern as the iostream library:
operator void*() const
{
return ( is_null ) ? 0 : this;
}void *Test() const
{
return ( is_null ) ? 0 : this;
} Member of: Beautiful Code Club.


