C++ question, classes, arrays, pointers etc..

Started by
1 comment, last by Zahlman 15 years, 6 months ago
Hello, this is my first post here, been lurking around a while and decided to start learning how to program; with that comes my first logjam :P So i am writing a program which simulates a bank account. First the menu pops up: cout << "1. Create New Account \n"; cout << "2. View Existing Accounts \n"; cout << "3. Delete Accounts \n"; cout << "Q. Quit \n"; cout << "Please Make an Option from above: "; when the user selects option 1: cout << "Account Number: \n"; is displayed. when the user inputs a 9 digit account number, I want this number to become an object of the class Bankact and I want it to be stored in a 2 dimensional array (vertical for each account, horizontal for each account number). I just don't really know how to do it. Thanks in advance. Code Below: EDIT: are there any tags like for this kind of stuff? class Bankact { public: char[10] account_number; //Account Number int balance; //Account Balance }; void ShowMenu(); //Show menu void Menu(char selection); // Menu List bool isvalid(char ch); void ShowMenu() { cout << "1. Create New Account \n"; cout << "2. View Existing Accounts \n"; cout << "3. Delete Accounts \n"; cout << "Q. Quit \n"; cout << "Please Make an Option from above: "; } void Menu(char selection) { switch(selection) { case '1': cout << "Account Number: \n"; break; case '2': cout << "Test 2 \n"; break; case '3': cout << "Test 3 \n"; break; default: cout << "Please choose an option.\n"; } cout << "\n"; } bool isvalid(char ch) { if(ch < '1' || ch > '3' && ch != 'q') return false; else return true; } //Program Begins here. int main() { char choice; for(;;) { do { ShowMenu(); cin >> choice; //Option selected by user } while(!isvalid(choice)); if(choice == 'q') break; //Quit Program cout << "\n"; Menu(choice); } return 0; }
Advertisement
Use [source][/source] tags to get pretty code boxes.

You could do what you want with a dynamic container, such as std::vector. It doesn't need to be two dimensional, the account instances already store the account number. In any case, in a two dimensional array all elements must have the same type.
#include <iostream>#include <vector>#include <string>class Account{public:	int balance;        // On most systems, an int is fine for what you are trying to do.        // A 32 bit integer can hold the kind of range you are talking about        // Unfortunately C++ does not make the kind of guarentees you would need        // about the range of values an int can hold.        // A more robust solution would be to use std::string.        // While a char array can be made to work, there is a higher potential        // for bugs.	int number;        // In case you haven't seen this before: this is a constructor        //        // It ensures that every account has a number and an        // initial balance of 0.        // the syntax used below is called an "initialiser list" 	Account(int number)	: 		balance(0),		number(number)	{	}};// In C++, one often uses typedefs when using templated container classes// There are numerous benefits://  * Container-agnostic code//    You can switch between equivalent containers easily//    i.e. by changing a single line//    e.g. vector, list and deque could probably all work heretypedef std::vector<Account> Bank;// Like isvalid(), making helper functions is a very good practise.// This function returns "true" if there is an account with that number// in the bank alreadybool account_exists(const Bank &bank, int number){    // This is to demonstrate that you can use std::vector     // as if it were an array, yet at the same time you can query    // it's current size.    // It would be more idiomatic to use std::vector::const_iterator for this    // loop, but for now I wish to keep it simple.    for(Bank::size_type i = 0 ; i < bank.size() ; ++i )    {        if(bank.number == number)        {			return true;        }    }    return false;}int main(){	Bank bank;        // This is just to give me a chance to run the program - for testing.	const std::string choice = "create account";	while(true)	{		// ...		if(choice == "create account")		{			int  number;			std::cout << "enter a new account number: ";                        // Half hearted error checking.                        // I will give you a link to somewhere                        // where this is discussed in depth			if(std::cin >> number)			{				if(!account_exists(bank,number))				{					Account account(number);					bank.push_back(account);				}				else				{					std::cerr << "account with number " << number << " already exists!\n";				}			}		}	}}


As mentioned above, it is more usual to use iterators when traversing a standard container. Here is the loop written with iterators:
bool account_exists(const Bank &bank, int number){    for(Bank::const_iterator it = bank.begin() ; it != bank.end() ; ++it )    {        const Account &account = *it;        if(account.number == number)        {	    return true;        }    }    return false;}

In this case I use const_iterators, because we do not intend to modify the accounts in this function.

In fact, the standard library includes many prefined functions (appropriately called algorithms), which can simplify this procedure. While in this case they are spectacular overkill, maybe by showing you the code I can give you a taste of what is possible.

The other thing that changes in this example is making the member variables of Account private. This is good practise in general for classes which have invariants. An Account can have lots of invariants - though to model them you would probably need to introduce some additional members, so I will refrain from that here.

To allow us to search our vector and maintain privacy I have introduced a member function to help. There is also an example of a "functor", which is a structure or class that overloads operator(). This means that you can treat it as if it were a function. However, it can also have state, which is perfect for our uses. In this case, we can use it to "remember" which account number we are looking for. We then use std::find_if() to locate the account.
#include <iostream>#include <vector>#include <string>#include <algorithm>class Account{private:	int balance;	int number;public:	Account(int number)	: 		balance(0),		number(number)	{	}	bool has_number(int number) const	{		return this->number == number;	}};typedef std::vector<Account> Bank;struct has_number{	int number;	has_number(int number) : number(number) {}	bool operator()(const Account &account) const	{		return account.has_number(number);	}};// std::find_if(begin, end, predicate)// This function will return an iterator to the first element in the range// [begin,end) that returns true when passed to predicate.// i.e. predicate(result) == true (Assuming a deterministic predicate)// // std::find_if() returns an element just outside the actual range of objects // if it cannot find such an element. The way it works is as follows.// Imagine we are using find_if on the array { 2, 1, 4, 3 }// This is how we pass the range:// -----------------------------// 2 1 4 3// |       |// |       +- end// +- begin// -----------------------------// So begin is at 2, and end is *one past* 3.// So when find_if cannot locate an appropriate instance, it returns "end".// bool account_exists(const Bank &bank, int number){	return std::find_if(bank.begin(),bank.end(),has_number(number)) != bank.end();}// ...


Of course, we could use a dedicated data structure that is designed exactly for this kind of use. It is called a "map" (or a "dictionary"), and provides efficient lookups, even when the number of accounts is large.

std::map::find() is very like find_if, it will return the "end" iterator if there is no such element in the map.

Note that there are two ways to insert a value into a map.
The first is: map[key] = value.
With this method, what happens is:
* the map attempts to locate key
* if key is found, the associated value is then overwritten with value
* if the key is missing, the map will construct a Value, and return it
* Again, this default-constructed value will be overwritten.

Our account instances are not default constructable, so that isn't an option (unless we choose to implement a default constructor). Instead, we use std::map::insert. This function takes a std::pair instance. Pair instances are ugly to construct, because we must explicitly mention the types involved:
Example:
Key key = ...Value value = ...map.insert(std::pair<Key,Value>(key,value));

We can instead use the helper function std::make_pair(), for which the compiler can deduce the template types:
Key key = ...Value value = ...map.insert(std::make_pair(key,value));

#include <iostream>#include <map>#include <string>#include <algorithm>class Account{private:	int balance;	int number;public:	Account(int number)	: 		balance(0),		number(number)	{	}};typedef std::map<int,Account> Bank;bool account_exists(const Bank &bank, int number){	return bank.find(number) != bank.end();}int main(){	Bank bank;	const std::string choice = "create account";	while(true)	{		// ...		if(choice == "create account")		{			int number;			std::cout << "enter a new account number: ";			if(std::cin >> number)			{				if(!account_exists(bank,number))				{					Account account(number);					bank.insert(std::make_pair(number,account));				}				else				{					std::cerr << "account with number " << number << " already exists!\n";				}			}		}	}}


Finally, I can't really see any use for pointers in your program. If you find yourself trying to do something and the only solution you can think of involves pointers, you are probably doing something wrong [smile]
Quote:Original post by ironlynxtk
Hello, this is my first post here, been lurking around a while and decided to start learning how to program


If you've been lurking here a while, and you're just learning to program, surely it should have occurred to you to try the "For Beginners" forum? You know, the one at the top of the list?

Quote:EDIT: are there any tags like for this kind of stuff?


Yes, in lowercase. Again - there's a 'faq' link on every page in the menubar at the top and the FAQ explains this kind of thing in a fair amount of detail.

Quote:when the user inputs a 9 digit account number, I want this number to become an object of the class Bankact


This is nonsensical. What you actually want to do is construct an instance of the class, which stores that number.

Quote:and I want it to be stored in a 2 dimensional array (vertical for each account, horizontal for each account number)


The instance already represents an entire account number, so there is no need for the array to be 2-dimensional. You make a 1-dimensional array of instances, each of which represents a number in its own array.

But there's no good reason to mess around with raw arrays here; instead, use the standard library class std::vector to store the objects, and std::string to store the account numbers.

Anyway, what's a "Bankact"? Is "act" supposed to be short for "account" somehow? 'acct' would be standard, but would it kill you to type it in full? For that matter, why not just "Account" - are you going to be dealing with any other kinds of accounts in your program?

Anyway, what you need to do is read the number from std::cin, and use it to construct the instance, and then append the instance to your vector. You can use the .push_back() member function of the vector for that purpose, and write a constructor for the class to create an instance (accept the account number as a parameter, and use the initialization list to set the account number to the passed-in string, and the balance to zero or something). Note that you don't need a temporary variable for this; something like 'myVector.push_back(Account(given_account_number))' works just fine.

This topic is closed to new replies.

Advertisement