#c
#c
Вопрос:
Примечание: Прошу прощения, если на эту программу больно смотреть. это моя первая программа, использующая производные классы. Я планирую отправить его на code review после того, как я заставлю его делать то, что я хочу, чтобы я мог получить советы о том, как его можно было бы разработать лучше. Не стесняйтесь приводить эти советы здесь, но моя главная цель в этом посте — заставить его работать правильно.
Эта программа обрабатывает снятие средств и депозиты для банковских счетов, а затем применяет процентную ставку.
проблема: По какой-то причине кажется, что значение checkingBalance застревает и не обновляется до нового значения на протяжении остальной части цикла при обработке учетных записей типа класса «CheckingAccount». Это странно, потому что тот же цикл используется для обработки другого производного класса «SavingsAccount», и он работает нормально, а одна из учетных записей типа «CheckingAccount», похоже, даже была обработана правильно, поэтому я полностью сбит с толку относительно того, что происходит.
Если бы кто-нибудь мог протянуть руку помощи и помочь мне решить эту проблему, я был бы очень признателен. Пожалуйста, не стесняйтесь просить меня что-либо уточнить, я отвечу как можно скорее.
считываемые данные:
Вывод:
Код, в котором, как я подозреваю, проблема, заключается:
//This loop is exited early:
for (int j = 0; j < transactionNum; j )
{
inFile >> transactionTypeTemp >> amountTemp;
inFile.get(discard);
ba.applyTransaction(accountType, transactionTypeTemp, amountTemp, j);
}
//when something in here occurs?
// c = j from loop
double checkingAccount:: applyTransaction(char transactionTypeTemp, int amountTemp, int c, double checkingBalance)
{
if (transactionTypeTemp == 'D')
{
checkingBalance = checkingBalance amountTemp;
}
else if (transactionTypeTemp == 'W')
{
if (checkingBalance < amountTemp)
{
cout << "error: transaction number " << c 1 << " never occured due to insufficent funds." << endl;
}
else
{
checkingBalance = checkingBalance - amountTemp;
if(checkingBalance < minimumBalance) //if last transaction brought the balance below minimum balance
{
checkingBalance = (checkingBalance - serviceCharge); //apply service charge
}
}
}
return checkingBalance;
}
вся программа:
//header file
#include <iostream>
#include <fstream>
using namespace std;
class bankAccount
{
public:
bankAccount();
void setAccountInfo(int accountNumTemp, double balanceTemp);
void prePrint(char accountType);
void applyTransaction(char accountType, char transactionTypeTemp, int amountTemp, int j);
void applyInterest(char accountType);
void postPrint();
private:
int accountNumber;
double balance;
};
class checkingAccount: public bankAccount
{
public:
void prePrint(int accountNumber, char accountType, double checkingBalance);
checkingAccount();
double applyTransaction(char transactionTypeTemp, int amountTemp, int c, double checkingBalance);
double applyInterest(double checkingBalance);
private:
float interestRate;
int minimumBalance;
float serviceCharge;
};
class savingsAccount: public bankAccount
{
public:
void prePrint(int savingsAccountNumber, char accountType, double savingsBalance);
savingsAccount();
double applyTransaction(char transactionTypeTemp, int amountTemp, int c, double savingsBalance);
double applyInterest(double checkingBalance);
private:
float interestRate;
};
//class implementation .cpp file
bankAccount:: bankAccount()
{
accountNumber = 0;
balance = 0;
}
void bankAccount:: setAccountInfo(int accountNumTemp, double balanceTemp)
{
accountNumber = accountNumTemp;
balance = balanceTemp;
}
void bankAccount:: prePrint(char accountType)
{
if(accountType == 'C')
{
int checkingAccountNumber = accountNumber;
double checkingBalance = balance;
checkingAccount ca;
ca.prePrint(checkingAccountNumber, accountType, checkingBalance);
}
else if (accountType == 'S')
{
int savingsAccountNumber = accountNumber;
double savingsBalance = balance;
savingsAccount sa;
sa.prePrint(savingsAccountNumber, accountType, savingsBalance);
}
}
void bankAccount:: applyTransaction(char accountType, char transactionTypeTemp, int amountTemp, int j)
{
int c;
c = j;
double checkingBalance, savingsBalance;
checkingAccount ca;
savingsAccount sa;
if (accountType == 'C')
{
checkingBalance = balance;
balance = ca.applyTransaction(transactionTypeTemp, amountTemp, c, checkingBalance);
}
else if (accountType == 'S')
{
savingsBalance = balance;
balance = sa.applyTransaction(transactionTypeTemp, amountTemp, c, savingsBalance);
}
}
void bankAccount:: applyInterest(char accountType)
{
double checkingBalance, savingsBalance;
checkingAccount ca;
savingsAccount sa;
if (accountType == 'C')
{
checkingBalance = balance;
balance = ca.applyInterest(checkingBalance);
}
else if (accountType == 'S')
{
savingsBalance = balance;
balance = sa.applyInterest(savingsBalance);
}
}
void bankAccount:: postPrint()
{
cout << "Balance after processing: " << balance << endl;
}
checkingAccount:: checkingAccount()
{
interestRate = .02;
minimumBalance = 500;
serviceCharge = 20;
}
void checkingAccount:: prePrint(int checkingAccountNumber, char accountType, double checkingBalance)
{
cout << "Account Number:" << checkingAccountNumber << " account type:" << accountType << " Starting Balance:" << checkingBalance << endl;
}
double checkingAccount:: applyTransaction(char transactionTypeTemp, int amountTemp, int c, double checkingBalance)
{
if (transactionTypeTemp == 'D')
{
checkingBalance = checkingBalance amountTemp;
}
else if (transactionTypeTemp == 'W')
{
if (checkingBalance < amountTemp)
{
cout << "error: transaction number " << c 1 << " never occured due to insufficent funds." << endl;
}
else
{
checkingBalance = checkingBalance - amountTemp;
if(checkingBalance < minimumBalance) //if last transaction brought the balance below minimum balance
{
checkingBalance = (checkingBalance - serviceCharge); //apply service charge
}
}
}
return checkingBalance;
}
double checkingAccount:: applyInterest(double checkingBalance)
{
checkingBalance = (checkingBalance (checkingBalance * interestRate));
return checkingBalance;
}
savingsAccount:: savingsAccount()
{
interestRate = .04;
}
void savingsAccount:: prePrint(int savingsAccountNumber, char accountType, double savingsBalance)
{
cout << "Account Number:" << savingsAccountNumber << " account type:" << accountType << " Starting Balance:" << savingsBalance << endl;
}
double savingsAccount:: applyTransaction(char transactionTypeTemp, int amountTemp, int c, double savingsBalance)
{
if (transactionTypeTemp == 'D')
{
savingsBalance = savingsBalance amountTemp;
}
else if (transactionTypeTemp == 'W')
{
if (savingsBalance < amountTemp)
{
cout << "error: transaction number" << c 1 << " never occured due to insufficent funds." << endl;
}
else
{
savingsBalance = savingsBalance - amountTemp; //apply transaction
}
}
return savingsBalance;
}
double savingsAccount:: applyInterest(double savingsBalance)
{
savingsBalance = (savingsBalance (savingsBalance * interestRate));
return savingsBalance;
}
//main .cpp file
int main()
{
ifstream inFile;
int numberOfAccounts, accountNumTemp, transactionNum, amountTemp;
double balanceTemp;
char discard, accountType, transactionTypeTemp;
bankAccount ba;
cout << "Processing account data..." << endl;
inFile.open("Bank.txt");
if (!inFile)
{
for (int a = 0; a < 20; a )
cout << endl;
cout << "Cannot open the input file."
<< endl;
return 1;
}
inFile >> numberOfAccounts;
inFile.get(discard);
for (int i = 0; i < numberOfAccounts; i )
{
inFile >> accountNumTemp >> accountType >> balanceTemp >> transactionNum;
inFile.get(discard);
ba.setAccountInfo(accountNumTemp, balanceTemp);
ba.prePrint(accountType);
for (int j = 0; j < transactionNum; j )
{
inFile >> transactionTypeTemp >> amountTemp;
inFile.get(discard);
ba.applyTransaction(accountType, transactionTypeTemp, amountTemp, j);
}
ba.applyInterest(accountType);
ba.postPrint();
}
inFile.close();
return 0;
}
Вот входные данные в форме, доступной для копирования и вставки, чтобы другим не приходилось вводить их в:
6
35231 C 500 3
W 250
W 200
W 100
46728 S 2700 2
D 250
W 100
87324 C 500 3
D 300
W 100
W 150
79873 S 800 0
89932 C 3000 2
W 1000
W 1500
98322 C 750 6
D 50
W 75
W 100
W 25
W 30
W 75
Комментарии:
1. Вы пробовали пошагово изменять код с помощью отладчика? Постарайтесь свести код к наиболее релевантным разделам.
2. И старые добрые инструкции print. Вам следует попробовать распечатать значения, которые он считывает.
3. хм, инструкции print показывают, что цикл не завершается рано, как я подозревал, значение просто случайным образом перестает изменяться.
4.я понимаю. Я почти уверен, что у нас один и тот же код. Я все еще хотел бы проверить часть данных. Можете ли вы что-нибудь сделать с контрольной суммой файлов (возможно, используя инструмент подобный этим)
Ответ №1:
Хм … выполнив быструю компиляцию и запуск с вашими данными, я получаю следующий вывод:
Processing account data...
Account Number:35231 account type:C Starting Balance:500
error: transaction number 3 never occured due to insufficent funds.
Balance after processing: 10.2
Account Number:46728 account type:S Starting Balance:2700
Balance after processing: 2964
Account Number:87234 account type:C Starting Balance:500
Balance after processing: 561
Account Number:79873 account type:S Starting Balance:800
Balance after processing: 832
Account Number:89832 account type:C Starting Balance:3000
Balance after processing: 510
Account Number:98322 account type:C Starting Balance:750
Balance after processing: 484.5
Большая часть этого выглядит для меня довольно разумно. Делая быстрое дополнение в моей голове, «недостаточно средств» кажется разумным для первой учетной записи, поскольку вы предоставляете ей начальный баланс в 500, а снятие средств в сумме составляет 550.
Выражаясь так красиво, как я умею, я не думаю, что написал бы код так же, как вы, но вывод, который я получаю, похоже, не имеет почти тех же проблем, которые вы показали. Это могло быть следствием неопределенного поведения (например, использования неинициализированной переменной), но я не потратил достаточно времени на просмотр кода, чтобы быть уверенным. Лично я думаю, что предпочел бы потратить свое время на другой дизайн, чем на поиск возможных ошибок в коде в его нынешнем виде прямо сейчас.
Редактировать: Что касается перепроектирования кода, когда у вас есть код, который должен быть реализован по-разному в двух производных классах, вы хотите использовать виртуальную функцию и соответствующим образом реализовать ее в каждом производном классе. Вместо того, чтобы создавать банковские счета в качестве локальных переменных в каждой функции-члене и создавать новый объект банковского счета в каждой, я бы создал один объект учетной записи, когда я читаю данные, определяющие учетную запись, а затем использовал этот объект учетной записи для всех транзакций по этому объекту.
Также, похоже, возникает значительный вопрос, действительно ли вам вообще нужен базовый класс и производные классы. Я не смотрел слишком внимательно, но мне кажется, что поведение двух типов учетных записей в значительной степени (если не полностью) идентично. Единственное отличие, которое я увидел, заключалось в том, что сберегательный счет не разрешает вывод средств в отрицательные числа, в то время как текущий счет разрешает такое вывод средств, но взимает плату за обслуживание. Хотя вы и не моделируете это, если баланс текущего счета упадет ниже некоторой точки, я почти уверен, что они также начнут отказываться от проверок.
В таком случае, мне кажется, что вы могли бы смоделировать все это как один тип учетной записи, но с двумя минимальными уровнями и платой, связанной с нарушением каждого. Первый уровень означает, что транзакция разрешена, но взимается комиссия. Второе означает, что транзакция не разрешена.
Для сберегательного счета оба уровня и плата устанавливаются равными $ 0.0. Для текущего счета уровень «взимания комиссии» составляет 0,0 доллара, а уровень «отказа в транзакции» (скажем) — 50 долларов. За оба варианта плата составляет, скажем, 50 долларов.
Комментарии:
1. Что ж, ваш вывод верен. Можете ли вы дать несколько советов о том, с чего мне следует начать с улучшения дизайна?
Ответ №2:
Крик.
Код на самом деле не кажется неправильным, поэтому вам может потребоваться просмотреть данные — например, вы переместили этот файл с помощью ftp и перепутали окончания строк.
Рефакторинг этого… Для начала просто ужасно писать свою собственную версию virtual с использованием if / else. Второе обновление переменных класса путем передачи их функциям-членам и обновления в результате приводит к путанице процесса.
Начиная с BankAccount ba; вы повторно используете это для каждой учетной записи, это затрудняет запись и чтение остальной части программы. Вместо этого используйте метод Factory / Lookup.
for (int i = 0; i < numberOfAccounts; i )
{
inFile >> accountNumTemp >> accountType >> balanceTemp >> transactionNum;
inFile.get(discard);
bankAccount amp;ba = bankAccount::getAccount(accountType, accountNumTemp, balanceTemp);
Без создания полноценной фабрики это может выглядеть примерно так
class bankAccount
{
protected:
bankAccount(int accountNumTemp, double balanceTemp);
public:
static bankAccountamp; getAccount( char accountType, int accountNumTemp, double balanceTemp){
switch(accountType)
case 'C':
return checkingAccount(accountNumTemp, balanceTemp);
case 'S':
return savingsAccount(accountNumTemp, balanceTemp);
default:
throw exception();
}
}
void prePrint();
void postPrint();
virtual string type() = 0;
virtual void applyTransaction( char transactionTypeTemp, int amountTemp, int j) = 0;
virtual void applyInterest() =0;
protected:
int accountNumber;
double balance;
};
Тогда в качестве примера дочерние классы изменились бы следующим образом
class checkingAccount: public bankAccount
{
public:
checkingAccount(int accountNumTemp, double balanceTemp)
: bankAccount(accountNumTemp, balanceTemp)
{ // these are currently unchanged, could probably move to static
interestRate = .02;
minimumBalance = 500;
serviceCharge = 20;
}
void applyTransaction(char transactionTypeTemp, int amountTemp, int c );
void applyInterest();
private:
float interestRate;
int minimumBalance;
float serviceCharge;
};
void bankAccount::prePrint()
{
cout << "Account Number:" << accountNumber
<< " account type:" << type()
<< " Starting Balance:" << balance << endl;
}
string checkingAccount::type(){ return "C" }
void checkingAccount:: applyTransaction(char transactionTypeTemp, int amountTemp, int c )
{
if (transactionTypeTemp == 'D')
{
balance = balance amountTemp;
}
else if (transactionTypeTemp == 'W')
{
if (balance < amountTemp)
{
cout << "error: transaction number " << c 1 << " never occured due to insufficent funds." << endl;
}
else
{
balance = balance - amountTemp;
if(balance < minimumBalance) //if last transaction brought the balance below minimum balance
{
balance = (balance - serviceCharge); //apply service charge
}
}
}
}
void checkingAccount:: applyInterest()
{
balance = (balance (balance * interestRate));
}
Ответ №3:
Что ж, ваш вывод верен. Можете ли вы дать несколько советов о том, с чего мне следует начать с улучшения дизайна?
Я бы начал с
- модульные тесты
- отладчик
и продолжайте с
- valgrind
Обновить
Я только что выполнил шаг 3 (valgrind), и он просто работает, предоставляя тот же вывод, что и Jerry:
Processing account data...
Account Number:35231 account type:C Starting Balance:500
error: transaction number 3 never occured due to insufficent funds.
Balance after processing: 10.2
Account Number:46728 account type:S Starting Balance:2700
Balance after processing: 2964
Account Number:87324 account type:C Starting Balance:500
Balance after processing: 561
Account Number:79873 account type:S Starting Balance:800
Balance after processing: 832
Account Number:89932 account type:C Starting Balance:3000
Balance after processing: 510
Account Number:98322 account type:C Starting Balance:750
Balance after processing: 484.5
Обратите внимание, что это с этим точным вводом (я обнаружил, что ввод довольно чувствителен, например, к изменениям регистра):
6
35231 C 500 3
W 250
W 200
W 100
46728 S 2700 2
D 250
W 100
87324 C 500 3
D 300
W 100
W 150
79873 S 800 0
89932 C 3000 2
W 1000
W 1500
98322 C 750 6
D 50
W 75
W 100
W 25
W 30
W 75
Комментарии:
1. Добавлены результаты valgrind и ссылочный вывод И ВВОД 🙂
2. Да, я не понимаю, почему вывод отличается для меня? Это то же самое, даже когда я запускаю этот код на других компьютерах в моем доме, но отличается для всех вас. Почему это должно быть? Мои данные те же
3. Как сказал другой человек, что-то могло сломаться при передаче. Является ли отказывающая машина другого типа (другая ОС, компилятор?) Вы могли бы использовать сумму входных данных MD5 в обоих местах, чтобы убедиться, что нет двоичной разницы (Notepad имеет тенденцию вставлять маркеры спецификации, которые ваш stdlib может не обрабатывать) … подобные волосатые вещи
4. На одной машине установлена 62-разрядная версия win 7, на другой — 32-разрядная версия win xp. я пробовал компилировать в codeblocks и dev c на компьютере с win 7 и dev c на компьютере с win xp
5. Удачи в поиске разницы. Приветствия!