[英]Sorting and using a binary search on a vector of object pointers
我正在嘗試編寫一些 c++ 代碼,它將對 object 指針的向量執行二進制搜索。 作為參考,我的程序就像一個銀行賬戶系統,用戶可以在其中開設銀行賬戶並在所述銀行賬戶上進行存款/取款,進而創建一個新交易 object 被推回到交易指針對象列表,如下所示:在 main.cpp 中:
else if (userCommand == "withdraw")
{
try
{
int index;
float amount;
time_t currentTime = time(nullptr); //gets current time
cout << "Please pick the account you wish to withdraw from starting from 1: " << endl;
cin >> index;
if (index > allAccounts.size() || index <= 0) throw InvalidIndexException(); //0 is not an option for any other command other than withdraw so we include it for the exception
cout << "Please give the amount of money you wish to withdraw: " << endl;
cin >> amount;
if (amount < 0) throw NegativeValue("Withdrawal");
allAccounts[index - 1]->withdraw(amount);
Transaction* accountTransaction = new Transaction("Withdrawal", currentTime, amount); //creates new Transaction object that will be passed into the account object
allAccounts[index - 1]->addTransaction(accountTransaction);
}
catch (ExceedOverDraftException)
{
cout << "Current account overdraft exceeded" << endl;
}
catch (NegativeWithdrawalException)
{
cout << "Savings account cannot go below 0" << endl;
}
catch (InvalidIndexException)
{
cout << "Index given does not exist" << endl;
}
catch (NegativeValue e)
{
cout << e.getMessage() << endl;
}
}
else if (userCommand == "deposit")
{
try
{
int index;
float amount;
time_t currentTime = time(nullptr);
cout << "Please pick the account you wish deposit to starting from 1: " << endl;
cin >> index;
if (index > allAccounts.size() || index <= 0) throw InvalidIndexException();
cout << "Please give the amount of money you wish to deposit: " << endl;
cin >> amount;
if (amount < 0) throw NegativeValue("Deposit");
allAccounts[index - 1]->deposit(amount);
Transaction* accountTransaction = new Transaction("Deposit", currentTime, amount);
allAccounts[index - 1]->addTransaction(accountTransaction);
}
catch (InvalidIndexException)
{
cout << "Index does not exist" << endl;
}
catch (NegativeValue e)
{
cout << e.getMessage() << endl;
}
}
else if (userCommand == "search")
{
try
{
int index;
float valueAnswer;
int transactionsSize;
cout << "Please say which account you wish to search transaction for starting from 1" << endl;
cin >> index;
if (index > allAccounts.size()) throw InvalidIndexException();
transactionsSize = allAccounts[index - 1]->getHistorySize();
cout << "Please state what value you wish to search for" << endl;
cin >> valueAnswer;
cout << allAccounts[index - 1]->search(valueAnswer, 0, transactionsSize - 1);
}
catch (InvalidIndexException)
{
cout << "Index given does not exist";
}
catch (TransactionNotFound e)
{
cout << e.getMessage();
}
}
在 account.cpp 中:
void Account::addTransaction(Transaction* t)
{
history.push_back(t); //as the vector is full of pointers the parameter must also be a pointer.
sort(history.begin(), history.end());
}
Transaction Account::search(float value, int start, int end) throw (TransactionNotFound) //search function is a binary search
{
if (start <= end)
{
for (int i = 0; i < history.size(); i++)
{
cout << history[i]->getValue() << endl;
}
int mid = (start + end) / 2; //midpoint of the vector
if (history[mid]->getValue() == value) return *history[mid];
else if (history[mid]->getValue() < value) return search(value, mid + 1, end);
else if (history[mid]->getValue() > value) return search(value, start, mid - 1);
}
else throw TransactionNotFound(value); //if the array has been searched to the point that start has reached the end the transaction doesn't exist
}
如您所見,主要思想是每次進行新交易時,無論是調用存款還是提款,都會進行一筆交易的 object,然后將其添加到向量中,然后按升序排序。 為了排序,我嘗試遵循使用結構對象而不是 class 對象( 對自定義對象的向量進行排序)的舊堆棧溢出消息中的指南,這似乎使用了結構 object 上的 < 運算符的運算符重載,然后允許要排序的結構對象的向量,我嘗試在我的事務 class 作為朋友 function 中進行調整,如下所示:在 transaction.cpp 中:
bool operator <(const Transaction& t1, const Transaction& t2)
{
return (t1.value < t2.value); //checks if the next value in the vector is greater than the current value in the vector.
}
但是,我已經測試了 sort 方法,它似乎沒有像我期望的那樣對對象進行排序,因為它似乎根本沒有對對象進行排序,導致我的 TransactionNotFound 異常被拋出。 我真的不確定我做錯了什么,我假設這是我完成操作員超載的方式,但我不知道我需要做些什么來修復它。 抱歉,如果我的代碼看起來很糟糕,我想我意識到我的 try 和 catch 塊可以 go 在 if 語句之外。 另外,如果有任何關於我的代碼可能需要的信息,請告訴我,我通常是堆棧溢出的新手,所以我不確定我所包含的內容是否需要。 人們可以提供的任何幫助都非常感謝!
除了許多設計缺陷之外,您還有幾個主要的語義錯誤。
第一,也是最重要的,也是排序不起作用的原因:
您將指針存儲在std::vector
“歷史”中。 但是在比較 function 中,您比較的是引用,所以比較的是值而不是指針。
因此,排序 function 不會根據存儲在事務中的值進行排序,而是根據它們在 memory 中的地址進行排序,您不知道並且可能是隨機的(但不受您的控制),
如果您一個接一個地創建一個事務,那么它們可能會使用升序的 memory 地址創建。 然后std::sort
什么也不做。 但這純屬巧合。
因此,您需要更改自定義排序 function。 為了簡單起見,我將使用 Lambda。 那么sort
語句將如下所示:
std::sort(history.begin(), history.end(),
[](const Transaction* t1, const Transaction* t2) {return t1->value < t2->value; });
然后排序 function 會做你想要的。
2、搜索function執行錯誤。
遞歸的停止條件不正確。 它必須是if (end < start)
。 中間的計算也是錯誤的。 用一張紙畫一幅畫,然后你就會看到它。 正確的說法是:
int mid = start + (end-start) / 2;
您總是需要添加新的起始值。 否則你永遠無法達到區間的上半部分。
然后,一個非常重要的聲明。 您不能依賴浮點類型的相等比較。 因此,如果要使用貨幣進行計算,請不要使用浮點或雙精度值。 使用 integer 類型 * 100。
現在讓我們看一個最小可重現的例子:
#include <iostream>
#include <string>
#include <ctime>
#include <vector>
#include <algorithm>
struct Transaction {
std::string type{};
time_t time{};
int value{};
int getValue() { return value; }
};
struct Account {
std::vector<Transaction*> history{};
void addTransaction(Transaction* t) {
history.push_back(t);
std::sort(history.begin(), history.end(),
[](const Transaction* t1, const Transaction* t2) {return t1->value < t2->value; });
}
Transaction* search(float value) {
return bsearch(value, 0, history.size()-1);
}
Transaction* bsearch(float value, int start, int end)
{
for (unsigned int i = 0; i < history.size(); i++) {
// std::cout << history[i]->getValue() << '\n';
}
if (end < start) return nullptr;
int mid = start + (end-start) / 2; //
if (history[mid]->getValue() == value)
return history[mid];
else if (history[mid]->getValue() < value) return bsearch(value, mid + 1, end);
else return bsearch(value, start, mid - 1);
}
};
int main() {
Account account{};
// Add some demo transactions
account.addTransaction(new Transaction{ {},{},9 });
account.addTransaction(new Transaction{ {},{},8 });
account.addTransaction(new Transaction{ {},{},7 });
account.addTransaction(new Transaction{ {},{},6 });
account.addTransaction(new Transaction{ {},{},5 });
account.addTransaction(new Transaction{ {},{},4 });
account.addTransaction(new Transaction{ {},{},3 });
account.addTransaction(new Transaction{ {},{},2 });
account.addTransaction(new Transaction{ {},{},1 });
Transaction* searchResult{};
searchResult = account.search(4);
if (searchResult)
std::cout << "\nFound: " << searchResult->getValue() << '\n';
else
std::cout << "\nError: 4 not found\n\n";
searchResult = account.search(42);
if (searchResult)
std::cout << "\nFound: " << searchResult->getValue() << '\n';
else
std::cout << "\nError: 42 not found\n\n";
return 0;
}
所以,也許你明白了。
但現在,更重要的是。 設計缺陷。
您正在使用大量異常。 你可以像 if 語句一樣使用它們。
不要這樣做。 對異常問題使用異常,而不是可以通過簡單的 if 語句處理的小問題。
您應該使用面向 C++ 和 object 的編程。
“存款”和“取款”都是交易。 因此,創建一個基礎 class “交易”並從中派生一個 class “存款”和一個 class “提款”。
不要對擁有的 memory 使用原始指針,而是使用std::unique_ptr
。 否則,您會造成大量 memory 泄漏。 如果你寫new
,那么,你也需要寫delete
。
或者使用智能指針,比如 unique_ptr。 這將為您完成工作。
一般來說,一個好的提示是,始終進行設計,例如在一張紙上或然而,然后開始編程。
不管怎樣,請繼續你的好工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.