You want to build a program but where to start?

You want to build a program but where to start?
OK, I'll tell you. This should be done in the following steps:
1. Specifications
2. Design
3. Implementation
4. Test & Debug
5. Documentation

We go through this article by an example program. Imagine we are ordered to make a program for a school. They need a program to store student's name and average of marks. Then they should be able to find a student's average by knowing his/her name and vice versa. The program should also be able to show all the averages on the screen, alphabetically sorted.

OK. Now we know what they want, so we can enter step one, Specifications:

• Program should start with a menu with the following options: 1) Show list 2) Enter new name 3) Change a mark 4) Delete an entry 5) Search by name 6) Search by mark 7)Exit
• The program should be able to do all the tasks in menu
• The program should keep the records (names and marks) on hard disk in order to keep them safe on power off and retrieve them when program starts.
Now step two, Design:
It is the most important part of the development because a good design will make implementation easy and efficient while a bad one will make you cry (and program users may insult you!). How should we start? There are classic ways to design a program. For trivial programs like this, we use top down functional decomposition technique. We write pseudo codes to demonstrate the design. As we see in specifications, 6 functions are necessary. In addition there should be a function to read user choice and call appreciate function. We also need to save the records somewhere and read them again when the program starts. So we should have a main() like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main() 
{
   LoadDataFromFile();
    while(userChoice != 7){
     userChoice = GetUserChoice();
     switch (userChoice) {
          case 1: ShowList(); break;
          case 2: AddEntry(); break;
          case 3: ChangeMark(); break;
          case 4: Delete(); break;
          case 5: ShowMark(); break;
          case 6: ShowName(); break;
          case 7: break;
      }
   }
   SaveDataToFile();
   return 0;
}


main() gets user choice, calls the appreciate function and will loop until it gets the choice 7, which means exit. In this case main() returns and program terminates. main() also loads the data from a file and saves them on exit (just before return).

This is the function which reads data from a file. For the first time the program runs there is no file to be opened. So this function creates an empty file to store data.

1
2
3
4
5
6
7
8
9
10
LoadDataFromFile()
{
	if(fileExists){ 
OpenFile();
ReadFileToMemory();
}
	else 
CreateNewFile();
		
}


We need to sorts the list then prints each item in the list until reaches the end of list. We sort list every time an item is added, so we will always have a sorted list in memory.

1
2
3
4
ShowList()
{
for(int i = 0; i < listCount; ++i  ) cout << i << "\t" << listItem[i];
}


To add an entry, program asks for name and mark of the student. There is an integer named listCount that keeps the number of records in the list.

1
2
3
4
5
6
7
8
AddEntry() 
{
	name = GetStudentName();
	mark = GetStudentMark();
listCount++;
	AddToList(name, mark);		
SortList();
}


Each entry (name and mark pair) in the list has an index number. Suppose the user wants to change the mark of or delete an entry. She/He should first select the Show List option or using search options to find the desired entry and see its index. Then he/she can use Delete or Change Mark options which will ask for the index of desired entry. Every time an item is added, the list will be sorted. So indices may change.

1
2
3
4
5
6
ChangeMark()
{
	idx = GetStudentIdx();
	mark = GetStudentMark();
	SetNewMark(idx, mark);
}


We will discuss SetNewMark() later.

Functions below seem straightforward:

1
2
3
4
5
6
Delete()
{
	idx = GetStudentIdx();
	DeleteFromTheList(idx);
	listCount--;
} 



1
2
3
4
5
ShowName()
{
	mark = GetStudentMark();
	for(int i = 0; i < listCount; ++i  ) if(listItem[i].mark == mark) cout << i << "\t" << listItem.name;
}


1
2
3
4
5
ShowMark()
{
	name = GetStudentName();
	for(int i = 0; i < listCount; ++i  ) if(listItem[i].name == name) cout << i << "\t" << listItem.mark;
}


Sort function uses bubble sort algorithm to sort the list. Study its code in implementation step.

You can see some new functions have been revealed in pseudo code. Some of them are straightforward like GetStudentMark() and some have to get studied like AddToList(). To get into more detail of those functions, we should now think about how to keep the records in the memory.

It is a branch of IT that discusses the ways which should be used to store particular kinds of data. But as I don't want to enter that area, I choose a simple method. We will have a class that defines a pair of name and mark.

1
2
3
4
5
6
class StudentEntry
{
public:
	string name;
	int mark;
}


One entry is defined. To have a list we use an array of pointers to that class which keeps a pointer to each entry.

 
StudentEntry *entryList[max_student];


AddToList() is something like this:
1
2
3
4
5
AddToList(name, mark)
{
	entryList[entryCount] = new StudentEntry(name, mark);
	
}


You can read about "new" keyword in this web site, if you are not familiar with it.

And DeleteFromTheList() is like this:
1
2
3
4
5
6
if(entryCount != idx) 
		for(int i = idx; i < entryCount; i++) {
			entryList[i]->name = entryList[i+1]->name;
			entryList[i]->mark = entryList[i+1]->mark;
		} 
delete entryList[entryCount];


To delete an entry we just replace it by its successor and repeat this action till the end of list. We also must delete the last entry that is duplicated when the list is shifted up. If not we will have a memory leak.

You can see some things like (listItem[i].name == name) in the design pseudo code. I wrote them before deciding how exactly entries are stored into memory. We can rewrite it as (entryList[i]->name == name) now that we know we are using an array of pointers to class.

We have finished the design step. Now we exactly know how program works. We have actually written some parts of it.

Step three, Implementation:

Its time to write the code in the C++ language. As you noticed our pseudo code is nearly in C++ syntax but it needs to be polished to be a functional C++ program. I feel good to explain a little about the process of translating a cpp source code into an executable. First we write a code in cpp syntax and save it on hard disk. This cpp file is readable by humen of course. Next we give this file to a special executable, named "compiler". Compiler is a program that translates human readable data into machine readable data and saves it on hard disk as an object file (these are files with .obj extension). These files are not yet ready to be executed by the system. The reason is that they call many routines that are written in other files. For example the cout << operator is defined in a .lib or .dll file. There is another program named "linker" that copies the code from .lib or other .obj files and puts them in the target file (That is the .obj file with main() entry point). After this operation, the file is ready to be executed and will have the .exe extension.
So to make a program you first need to write its source code files somewhere, for example in Windows notepad, and save them, then give them to compiler. The compiler output is .obj of our .cpp files which will be given to linker in addition of needed .lib files to produce the final executive file.

It may seem complex and will be a real pain if you really try this actually. Because of this, there are programs named IDE (Integrated Development Environment) that make the work simple. They have usually an easy to use interface, they highlight cpp keywords, format document using spacing and indentation to make the code easy to read, and more important they do this compile and linking without bothering you. They do their job that smooth that you will not know a separate compiler and linker program exist and work independent of IDE program. IDEs usually also have some useful debug tools which helps us find bugs in program. Nevertheless you have to know how to use your IDE and we suppose you do. If you don't, please read its help and documentation. Now I make a new empty cpp file to write my code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include <iostream>
#include <string>
#include <fstream>
using namespace std; 
/////Declarations////////////////////////////////////////////////
const int MAX_STUDENT = 500;
const char FILE_PATH[] = "C:\\entry_file.txt";
typedef int INDEX;
typedef double MARK;
typedef string NAME;
int entryCount = -1;	//-1 means the list is empty
fstream entryFile;

class StudentEntry
{
public:
	StudentEntry(NAME name, MARK mark): name(name), mark(mark){}
	void operator = (StudentEntry *entry)
	{
		name = entry->name;
		mark = entry->mark;
	}
	NAME name;
	MARK mark;
}*entryList[MAX_STUDENT];

/////Function prototypes////////////////////////////////////////
INDEX GetStudentIdx();	//Gets number of index from user
MARK GetStudentMark();	//Gets number of maerk from user
NAME GetStudentName();	//Gets string of name from user
void DeleteFromTheList(INDEX idx);	//Deletes an item with index of idx
void Delete();	//Called by DeleteFromTheList(INDEX)
void ChangeMark();	//Changes the mark field of a record
void SetNewMark(INDEX idx, MARK mark);	//Called by ChangeMark()
void AddEntry();	//Adds new item to list
void AddToList(NAME name, MARK mark);	//Called by AddEntry()
void SortList();	//Does a buble sort on list
void ShowMark();	//Shows all marks with the same name
void ShowName();	//Shows all names with the same mark
void ShowList();	//Shows all the items in the list
int GetUserChoice();	//Gets numbet of option from user
void LoadDataFromFile();	//Loads data from a file
void SaveDataToFile();	//Saves data to a file

/////////////////////////////////////////////////////////////
INDEX GetStudentIdx()
{
	cout << "Enter index: ";
	INDEX idx;
	cin >> idx;
	return idx;
}

MARK GetStudentMark()
{
	cout << "Enter mark: ";
	MARK mark;
	cin >> mark;
	return mark;
}

NAME GetStudentName()
{
	cout << "Enter name: ";
	NAME name;
	cin >> name;
	return name;
}

void DeleteFromTheList(INDEX idx)
{
	if(entryCount != idx) 
		for(int i = idx; i < entryCount; i++) {
			entryList[i]->name = entryList[i+1]->name;
			entryList[i]->mark = entryList[i+1]->mark;
		} 
	delete entryList[entryCount];
}
void Delete()
{
	if(entryCount != -1){
		DeleteFromTheList(GetStudentIdx());
		entryCount--;
	}
} 

void SetNewMark(INDEX idx, MARK mark)
{
	entryList[idx]->mark = mark;
}

void ChangeMark()
{
	SetNewMark(GetStudentIdx(), GetStudentMark());
}

void AddToList(NAME name, MARK mark)
{
	entryList[entryCount] = new StudentEntry(name, mark);
}

void SortList()
{
	for(int i = 0 ; i < entryCount;i++){
		for(int j = 0 ; j < entryCount;j++)
		{
			if(entryList[j]->name.compare(entryList[j+1]->name ) == 1)
			{
				StudentEntry temp = *entryList[j+1];
				*entryList[j+1] = entryList[j];
				*entryList[j] = temp;

			}
		}
	}
}

void AddEntry() 
{
	entryCount++;
	NAME name = GetStudentName();
	AddToList(name , GetStudentMark());		
	SortList();
}

void ShowMark()
{
	NAME name = GetStudentName();
	for(int i = 0; i <= entryCount; ++i  ) if(entryList[i]->name == name) cout << i << "\t" << entryList[i]->mark << endl;
}

void ShowName()
{
	MARK mark = GetStudentMark();
	for(int i = 0; i <= entryCount; ++i  ) if(entryList[i]->mark == mark) cout << i << "\t" << entryList[i]->name <<endl;
}

void ShowList()
{
	for(int i = 0; i <= entryCount; ++i  ) cout << i << "\t" << entryList[i]->name << "\t" << entryList[i]->mark << endl;
}


int GetUserChoice()
{
	int choice;
	cout << "Enter the option's number and press enter: ";
	cin >> choice;
	return choice;
}

void LoadDataFromFile()
{
	entryFile.open(FILE_PATH,ios_base::in);
	if(entryFile.is_open()){
		cout << "File opened." << endl;
		char temp[100];
		for(entryCount = 0; entryFile >> temp; entryCount++)
		{
			entryList[entryCount] = new StudentEntry(temp, 0);
			entryFile >> entryList[entryCount]->mark;
		

		}
		entryCount--;
		entryFile.close();
		entryFile.clear();
	}
	else{
		entryFile.clear();
		cout << "File not found in " << FILE_PATH << endl;
	}
}

void SaveDataToFile()
{
	entryFile.open(FILE_PATH,ios_base::out);
	if(entryFile.is_open()){ 
		if(!entryFile.good())
		{
			cout << "Error writing file." << endl;
		}
		else
			for(int i =0 ;i<=entryCount;i++)
			{
				entryFile << entryList[i]->name<<endl;
				entryFile << entryList[i]->mark<< endl;
			}
		entryFile.close();
	}


}

int main() 
{
	LoadDataFromFile();
	int userChoice;
	do{
		cout <<"1: Show List\n2: Add Entry\n3: Change Mark\n4: Delete\n5: Search Name\n6: Search Mark\n7: Save and Exit\n";
		cout << "Current number of records: " << entryCount + 1<< endl;
		userChoice = GetUserChoice();
		switch (userChoice) {
			case 1: ShowList(); break;
			case 2: AddEntry(); break;
			case 3: ChangeMark(); break;
			case 4: Delete(); break;
			case 5: ShowMark(); break;
			case 6: ShowName(); break;
			case 7: break;
		}
	}while(userChoice != 7);
	SaveDataToFile();
	return 0;
} 


This step is done by now. You should read this code carefully and compare it to pseudo code. It is not a perfect implementation as it has many shortcomings:
- You cannot enter a pair of name and sir name, only one of them
- If you enter any thing other than a number when it asks for mark or index, program crashes
- There is no error handling in program
- When it asks for an option, user is able to enter things like: "asd" or "23423".
- You may find many other things similar

This is all because I tried to keep the code simple.
Step 4, Test and Debug:
The above code actually has passed this step because I couldn't place a buggy code in the site. For testing, I ran it many times and tried different input data. I had some problems with reading from a file, which was simply because I had forgotten to allocate memory for items that were read from the file (using new).

I gave the program to my sister in order to test it. She said your program does not accept marks with floating point like 12.5. Because of good implementation I just had to change
 
typedef int MARK;


To

 
typedef double MARK; 


The other problem I saw in my code was that marks were not linked to names. It was a bug in my SortList() function as it only sorted names and not the corresponding marks.

At last it couldn't save the data when the file was not opened in LoadDataFromFile(). It was because I didn't use clear() function which resets the stream flags after the file could not be opened. It was a beginner fault.

The code was tested and debugged on MSVC++ 2005 SP1.

Step 5, Documentation:

Documentation satisfies two peoples need; Developers and Users. Users need to know how to use the program, known issues, and how to troubleshoot. Developers need to know how the program works, how the design is, what the plug-in interface is (for programs that support), etc in order to develop the program in the future or for maintenance purposes. Using remarks to explain the ambiguous or key parts of the program is a very good practice. But additional design and implementation description should be written somewhere for further use.


Feel free to contact me.
Thanks, your modification works. However, would you mind explaining to me what some of it does? I am unclear on what (!(cin >> input)) means, what cin.clear() or cin.get() does, and why your while statement has no commands within it (it immediately terminates with a semicolon).

Thank you very much!
pdusen:

When cin (or std::cin) inputs the user's text into the variable (in this case, "input" is the variable name), it may occasionally have an error. Here is an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

// I don't like to use "using namespace std;" and prefer to just use
// things like, "std::cin" or "std::cout", but will use it in this example
// for simplicity
//
using namespace std;


int main(void)
{
   int userInputNumber;
   cout << "Enter a number: ";
   cin >> userInputNumber;

   cout << "You entered the number: " << userInputNumber << endl;

   return 0;
}


If a user were to input anything that wasn't a number (like a letter [a..z, A..Z], a symbol [$*%], etc.), the code would have problems.

The "cin" returns a 0 (zero) if there is a problem, such as invalid user input.

In siavoshkc's post, the "if (!(...))" is the same as saying "if ((...) == 0)". That way, if the user DID do something that you didn't expect, the whole program isn't affected.

The "clear()" function is related to the cin() error. If cin() DOES have some sort of error, then cin.clear() would make it so future uses of cin() can function correctly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std; // Again, ick...

int main(void)
{
   int userInputNumber;
   char userInputString[256];

   cout << "Enter a number: ";
   cin >> userInputNumber;

   cout << "Now enter a string of text: ";
   cin >> userInputString;

   cout << "You entered number (" << userInputNumber << ") and string (" << userInputString << ").\n";

   return 0;
}


If, in the first prompt for user input, the user typed in a non-number, the cin() would report an error (although no one was "listening" for that error). When it gets to the second portion where it prompts for a string of text, the cin() notices that it still has an error that wasn't resolved, so it doesn't actually collect any more information: it just basically skips the second cin() portion.

Now, if you were to test the first cin() and see that it had a bad value, you could clear that cin() error code by using "clear()". It's as simple as using "cin.clear()". It doesn't necessarily re-prompt the user for input, but at least at the next instance of cin(), it will wait for input instead of skipping past due to the "unresolved error". Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std; // Again, ick...

int main(void)
{
   int userInputNumber;
   char userInputString[256];

   cout << "Enter a number: ";
   cin >> userInputNumber;

   if (!cin)
   {
      cout << "Uh-oh.  You entered a bad value for the last prompt.  Try to do better this time...\n\n";
      cin.clear();
   }

   cout << "Now enter a string of text: ";
   cin >> userInputString;

   cout << "You entered number (" << userInputNumber << ") and string (" << userInputString << ").\n";

   return 0;
}


You'll notice that when outputting at the end, the "userInputNumber" reports a garbage value, but at least the string displays correctly (at least, it displays the letter(s) you entered for the number prompt).

Finally, cin.get() is explained here: http://www.cplusplus.com/reference/iostream/istream/get.html (actually, all these are explained on this site, but I thought some example code might be helpful).

Hope this helps!
Last edited on
this is really helpful for us beginners, hope more posts on this topic.
That's a really great and helpful topic for us-the beginners and I understood almost everything but one thing...I read one book for C++ and there the class defining was explained a little bit different and I couldn't get this part here so can you explain more specifically that part:
1
2
3
4
5
6
7
8
9
10
11
12
class StudentEntry
{
public:
	StudentEntry(NAME name, MARK mark): name(name), mark(mark){}
	void operator = (StudentEntry *entry)
	{
		name = entry->name;
		mark = entry->mark;
	}
	NAME name;
	MARK mark;
}*entryList[MAX_STUDENT];


I have no idea what the first and second line after "public" are doing and shouldn't we use constructors and destructors and shouldn't the two variables be in "Private" section?
The class definition is probably not the best example for beginners.
A revised (and hopefuly more obvious) version would be

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef double STUDENT_MARK;        //Avoid generic words for variables
typedef string STUDENT_NAME;

class StudentEntry
{
private:
	STUDENT_NAME name;         //Make members private
	STUDENT_MARK mark;
public:
	StudentEntry(STUDENT_NAME st_name, STUDENT_MARK st_mark)
           : name(st_name), mark(st_mark){}     //Constructor. Revised type and parameter names make code less obtuse
	void operator = (StudentEntry *entry)  //operator overload for '='
	{
		name = entry->name;
		mark = entry->mark;
	}
                void setName(STUDENT_NAME st_name) {name = st_name;}  //Making name and mark private
                STUDENT_NAME getName() {return name;}                 //means we need get and set methods
                void setMark(STUDENT_MARK st_mark) {mark = st_mark;}
                STUDENT_MARK getMark() {return mark;}
}*entryList[MAX_STUDENT];


As commented, the fist public declaration is the constructor.
Using the same name for the type and variable (but different case) is fine for the compiler, but more confusing to read.
The confusion was heightened by having the same name for the parameter and class member. Again, by using member initialisers for the compiler can distinguish between 'name' (the class member) and 'name' (the constructor parameter), but it's harder for someone reading the code - or perhaps more accurately it's easier for someone to make a mistake when reading the code!

The second line is an overloaded '=' opperator, to allow you to use '=' to assign one StudentEntry to another. Operator overloading is reasonably common but somethign that probably deservs a comment (documentation for developers!) when presented for beginners.

I have also modified it to make the members private - which is generaly regarded as good practice BUT it does mean we have to add get and set methods to access and modify the members - this would mean changing the rest of the code to use these.

The one thing this does show is that there are often multiple ways to do things in C++, and often the 'best' way will depend on circumstances - which is confusing for beginners!
The use of public vs private members is a good example of this. Making them public made the class simpler and use easier in the example, so could be said to be 'best'. But it is generally accepted that making them private with public methods to access and modify is 'best' - it allows the class to hide it's internal representation of the data (and thus allows the internal represetation to change without chanign teh use of the class) and provides a layer to incorporate validation of changes, for example.
Last edited on
Topic archived. No new replies allowed.