Wyrzucanie wyjątku o naruszeniu dostępu do odczytu

0

Witam, mam problem z moim programem. Można go włączyć,ale po dwukrotnym wyświetleniu pierwszej opcji(inne opcje jeszcze nie działają) zostaje wyrzucony wyjątek o naruszeniu dostępu do odczytu. W programie chciałbym użyć polimorfizmu, stąd też takie rozwiązanie. Jakby ktoś był w stanie mi wytłumaczyć co robię źle to byłbym wdzięczny. Z góry dziękuję.

#include<iostream>
#include<string>
#include<conio.h>
using namespace std;



class Screen
{
public:
	void displayMessage(string message)
	{
		cout << message;
	}

	void displayMessageLine(string message)
	{
		cout << message << endl;
	}

	void displayDollarAmount(double amount)
	{
		cout << "$" << amount;
	}
};

class Keypad
{
	int nr;
public:
	Keypad()
	{
		nr = 0;
	}
	int getInput()
	{
		cin >> nr;
		return nr;
	}
};





class Account
{
	int accountNumber;
	int pin;
	double availableBalance;
	double totalBalance;

public:
	Account(){}

	Account(int theAccountNumber, int thePIN, double theAvailableBalance, double theTotalBalane)
	{
		accountNumber = theAccountNumber;
		pin = thePIN;
		availableBalance = theAvailableBalance;
		totalBalance = theTotalBalane;
	}

	bool validatePIN(int userPIN)
	{
		if (userPIN == pin)
			return true;
		else
			return false;
	}

	double getAvailableBalance()
	{
		return availableBalance;
	}

	double getTotalBalance()
	{
		return totalBalance;
	}

	void credit(double amount)
	{
		totalBalance += amount;
	}

	void debit(double amount)
	{
		availableBalance -= amount;
		totalBalance -= amount;
	}

	int getAccountNumber()
	{
		return accountNumber;
	}
};


class BankDatabase
{
	Account*accounts;
public:
	BankDatabase()
	{
		accounts = new Account[2];
		accounts[0] = Account(12345, 54321, 1000.0, 1200.0);
		accounts[1] = Account(98765, 56789, 200.0, 200.0);
	}
	~BankDatabase()
	{
		delete[] accounts;
	}

private:
	Account*getAccount(int accountNumber)
	{
		Account*currentAccount = accounts;

		for(int i=0;i<2;i++)
		{
			if (currentAccount->getAccountNumber() == accountNumber)
			{
				return currentAccount;
			}
			currentAccount++;
		}
		return nullptr;
	}

public:

	bool authenticateUser(int userAccountNumber, int userPIN)
	{
		Account*userAccount = getAccount(userAccountNumber);
		

		if (userAccount != nullptr)
		{
			
			return userAccount->validatePIN(userPIN);
		}
		else
			return false;
	}

	double getAvailableBalance(int userAccountNumber)
	{

		return getAccount(userAccountNumber)->getAvailableBalance();
	}

	double getTotalBalance(int userAccountNumber)
	{
		return getAccount(userAccountNumber)->getTotalBalance();
	}

	void credit(int userAccountNumber, double amount)
	{
		getAccount(userAccountNumber)->credit(amount);
	}

	void debit(int userAccountNumber, double amount)
	{
		getAccount(userAccountNumber)->debit(amount);
	}
};


class Transaction
{
protected:
	int accountNumber;
	Screen*screen;
	BankDatabase*bankDatabase;

public:
	Transaction(int userAccountNumber, Screen &atmScreen,BankDatabase &atmBankDatabase)
	{
		screen = new Screen;
		bankDatabase = new BankDatabase;
		accountNumber = userAccountNumber;
		screen = &atmScreen;
		bankDatabase = &atmBankDatabase;
	}
	~Transaction()
	{
		delete screen;
		delete bankDatabase;
	}

	int getAccountNumber()
	{
		return accountNumber;
	}

	Screen*getScreen()
	{
		return screen;
	}

	BankDatabase*getBankDatabase()
	{
		return bankDatabase;
	}

	virtual void execute() = 0;
};


class BalanceInquiry : public Transaction
{
public:
	BalanceInquiry(int userAccountNumer, Screen atmScreen, BankDatabase atmBankDatabase):Transaction(userAccountNumer, atmScreen, atmBankDatabase)
	{
		screen = new Screen;
		bankDatabase = new BankDatabase;
	}
	~BalanceInquiry()
	{
		delete screen;
		delete bankDatabase;
	}
	

	void execute()
	{
	
		BankDatabase*bankDatabase =getBankDatabase();
		Screen*screen = screen=getScreen();
		
		double availableBalance = bankDatabase->getAvailableBalance(getAccountNumber());
		double totalBalance = bankDatabase->getTotalBalance(getAccountNumber());
		
		screen->displayMessageLine("\nBalance Information:");
		screen->displayMessage(" - Available balance:");
		screen->displayDollarAmount(availableBalance);
		screen->displayMessage("\n - Total balance:      ");
		screen->displayDollarAmount(totalBalance);
		screen->displayMessageLine("");
	}
};

class ATM
{
	bool userAuthenticated;
	int currentAccountNumber;
	Screen*screen;
	Keypad*keypad;
	BankDatabase*bankDatabase;
	Transaction*tmp;

	enum { BALANCE_INQUIRY=1, WITHDRAWAL=2, DEPOSIT=3, EXIT=4 };

public:
	ATM()
	{
		userAuthenticated = false;
		currentAccountNumber = 0;
		screen = new Screen;
		keypad = new Keypad;
		bankDatabase = new BankDatabase;
		tmp = nullptr;
	}
	~ATM()
	{
		delete screen;
		delete keypad;
		delete bankDatabase;
		delete tmp;
	}

	void run()
	{
		while (true)
		{
			while (!userAuthenticated)
			{
				screen->displayMessageLine("\nWelcome!");
				authenticateUser();
			}

			performTransactions();
			userAuthenticated = false;
			currentAccountNumber = 0;
			screen->displayMessageLine("\nThank you! Goodbye");
		}
	}

private:
	void authenticateUser()
	{
		screen->displayMessage("\nPlease enter your account number: ");
		int accountNumber = keypad->getInput();
		screen->displayMessage("\nEnter your PIN: ");
		int pin = keypad->getInput();
		
		userAuthenticated =bankDatabase->authenticateUser(accountNumber, pin);

		if (userAuthenticated)
		{
			currentAccountNumber = accountNumber;
		}
		else
			screen->displayMessageLine("Invalid account number or PIN. Please try again.");
	}

	void performTransactions()
	{
		
		bool userExited = false;

		while (!userExited)
		{
			Transaction*currentTransaction= nullptr;

			int mainMenuSelection = displayMainMenu();

			switch (mainMenuSelection)
			{
			case BALANCE_INQUIRY:
			case WITHDRAWAL:
			case DEPOSIT:

				currentTransaction = createTransaction(mainMenuSelection);

				currentTransaction->execute();
				break;
			case EXIT:
				screen->displayMessageLine("\nExiting the system...");
				userExited = true;
				break;
			default:
				screen->displayMessageLine("\nYou did not enter a valid selection. Try again.");
				break;
			}
		}
	}

	int displayMainMenu()
	{
		screen->displayMessageLine("\nMain menu:");
		screen->displayMessageLine("1-View my balance");
		screen->displayMessageLine("2-Withdraw cash");
		screen->displayMessageLine("3-Deposit funds");
		screen->displayMessageLine("4-Exit\n");
		screen->displayMessageLine("Enter a choice ");
		return keypad->getInput();
	}

	
	Transaction*createTransaction(int type)
	{
		switch (type)
		{
		case BALANCE_INQUIRY:
			tmp = new BalanceInquiry(currentAccountNumber,*screen, *bankDatabase);
			break;
		case WITHDRAWAL:
			break;
		}
		return tmp;
	}
};
0
4ever4 napisał(a):

Witam, mam problem z moim programem. Można go włączyć,ale po dwukrotnym wyświetleniu pierwszej opcji(inne opcje jeszcze nie działają) zostaje wyrzucony wyjątek o naruszeniu dostępu do odczytu.

To skutek.
Przyczyną może być niezainicjowany wskaźnik, przejechanie przez obszar tablicy itd... jak to w C

0

@4ever4: uruchom program pod debbugerem bez ustawionych pułapek, wtedy jak się wysypie będziesz widział w której linii kodu to nastapiło.
Ale generalnie na 90% jest tak jak pisze Anyktokolwiek - niezainicjalizowany wskaźnik, albo wskaźnik ustawiony na skasowany obiekt.

0

według debbugera program wysypuje się w następującej linii:
currentTransaction->execute();
Zauważyłem też, że gdy nie ma destruktora tablicy program się nie wysypuję, jednak wydaje mi się, że obiekt powinien być zniszczony żeby nie było wycieków pamięci.

0

Winowajcą zatem jest albo niewłaściwie ustawiony wskaźnik do currentTransaction, albo coś w exectute()

    Transaction*createTransaction(int type)
    {
        switch (type)
        {
        case BALANCE_INQUIRY:
            tmp = new BalanceInquiry(currentAccountNumber,*screen, *bankDatabase);
            break;
        case WITHDRAWAL:
            break; //<- to wygląda podejrzanie...
        }
        return tmp;//...bo może zwrócić niezainicjowany wskaźnik.
    }

ustaw break pointa na początek tej funkcji, i watchera na zmienną tmp

0
4ever4 napisał(a):
	Transaction(int userAccountNumber, Screen &atmScreen,BankDatabase &atmBankDatabase)
	{
		screen = new Screen;
		bankDatabase = new BankDatabase;    // przydzielasz pamięć
		accountNumber = userAccountNumber;
		screen = &atmScreen;
		bankDatabase = &atmBankDatabase;  // nadpisujesz adres, wyciek
	}
	~Transaction()
	{
		delete screen;
		delete bankDatabase;   // zwalniasz pamięć spod adresu ustawionego z referencji
	}

Ogółem to aż oczy bolą od nawału new/delete. Poczytaj o smart pointerach i kontenerach, ogółem zaprzyjaźnij się z STL.

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