对对象指针向量进行排序和使用二进制搜索
Sorting and using a binary search on a vector of object pointers
我正在尝试编写一些 C++ 代码来对对象指针向量执行二进制搜索。作为参考,我的程序就像一个银行账户系统,用户可以在其中开设一个银行账户并在该银行账户上进行 deposits/withdrawals,这反过来又会创建一个新的交易对象,该对象被推回交易指针对象列表,例如所以:
在 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
}
如您所见,主要思想是每次进行新交易时,无论是调用存款还是取款,都会创建一个交易对象,然后将其添加到向量中,然后按升序排序.为了进行排序,我尝试遵循使用结构对象而不是 class 对象(Sorting a vector of custom objects)的旧堆栈溢出消息的指南,该消息似乎在结构对象上使用 < 运算符的运算符重载然后允许对结构对象的向量进行排序,我尝试在我的交易 class 中将其调整为友元函数,如下所示:
在 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 块可以超出 if 语句。此外,如果可能需要有关我的代码的任何信息,请告诉我,我一般是堆栈溢出的新手,所以我不确定是否需要我包含的内容。
非常感谢人们可以提供的任何帮助!
除了许多设计缺陷外,您还有几个主要的语义错误。
第一个,也是最重要的,也是为什么排序不起作用的原因:
您将指针存储在 std::vector
“历史记录”中。但是在比较函数中,你比较引用,所以比较值而不是指针。
因此,排序函数不会根据存储在交易中的值进行排序,而是根据它们在内存中的地址进行排序,您不知道并且可能是随机的(但不受您的控制),
如果您一个接一个地创建事务,那么它们可能会以递增的内存地址创建。然后 std::sort
什么都不做。但这纯属巧合。
因此,您需要更改自定义排序功能。为了简单起见,我将使用 Lambda。那么 sort
语句将如下所示:
std::sort(history.begin(), history.end(),
[](const Transaction* t1, const Transaction* t2) {return t1->value < t2->value; });
然后排序函数将执行您想要的操作。
2、搜索功能实现错误
递归的停止条件不正确。必须是if (end < start)
。
mid 的计算也是错误的。用一张纸画一幅画,然后你就会看到它。正确的说法是:
int mid = start + (end-start) / 2;
您总是需要添加新的起始值。否则你永远无法到达区间的上半部分。
然后,一个非常重要的声明。您不能依赖浮点类型的相等比较。因此,如果要使用货币进行计算,则不要使用 float 或 double 值。使用整数类型 * 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++ 和面向对象编程。
“存款”和“取款”都是交易。因此,创建一个基础 class“交易”并从中派生一个 class“存款”和一个 class“取款”。
不要对拥有的内存使用原始指针,但是std::unique_ptr
。否则,您会造成大量内存泄漏。如果你写new
,那么,有些时候,你还需要写delete
。
或者使用智能指针,比如unique_ptr。这将为您完成工作。
总的来说,一个好的提示是,总是先做一个设计,例如在一张纸上,然后开始编程。
无论如何,请继续努力。
我正在尝试编写一些 C++ 代码来对对象指针向量执行二进制搜索。作为参考,我的程序就像一个银行账户系统,用户可以在其中开设一个银行账户并在该银行账户上进行 deposits/withdrawals,这反过来又会创建一个新的交易对象,该对象被推回交易指针对象列表,例如所以: 在 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
}
如您所见,主要思想是每次进行新交易时,无论是调用存款还是取款,都会创建一个交易对象,然后将其添加到向量中,然后按升序排序.为了进行排序,我尝试遵循使用结构对象而不是 class 对象(Sorting a vector of custom objects)的旧堆栈溢出消息的指南,该消息似乎在结构对象上使用 < 运算符的运算符重载然后允许对结构对象的向量进行排序,我尝试在我的交易 class 中将其调整为友元函数,如下所示: 在 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 块可以超出 if 语句。此外,如果可能需要有关我的代码的任何信息,请告诉我,我一般是堆栈溢出的新手,所以我不确定是否需要我包含的内容。 非常感谢人们可以提供的任何帮助!
除了许多设计缺陷外,您还有几个主要的语义错误。
第一个,也是最重要的,也是为什么排序不起作用的原因:
您将指针存储在 std::vector
“历史记录”中。但是在比较函数中,你比较引用,所以比较值而不是指针。
因此,排序函数不会根据存储在交易中的值进行排序,而是根据它们在内存中的地址进行排序,您不知道并且可能是随机的(但不受您的控制),
如果您一个接一个地创建事务,那么它们可能会以递增的内存地址创建。然后 std::sort
什么都不做。但这纯属巧合。
因此,您需要更改自定义排序功能。为了简单起见,我将使用 Lambda。那么 sort
语句将如下所示:
std::sort(history.begin(), history.end(),
[](const Transaction* t1, const Transaction* t2) {return t1->value < t2->value; });
然后排序函数将执行您想要的操作。
2、搜索功能实现错误
递归的停止条件不正确。必须是if (end < start)
。
mid 的计算也是错误的。用一张纸画一幅画,然后你就会看到它。正确的说法是:
int mid = start + (end-start) / 2;
您总是需要添加新的起始值。否则你永远无法到达区间的上半部分。
然后,一个非常重要的声明。您不能依赖浮点类型的相等比较。因此,如果要使用货币进行计算,则不要使用 float 或 double 值。使用整数类型 * 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++ 和面向对象编程。
“存款”和“取款”都是交易。因此,创建一个基础 class“交易”并从中派生一个 class“存款”和一个 class“取款”。
不要对拥有的内存使用原始指针,但是std::unique_ptr
。否则,您会造成大量内存泄漏。如果你写new
,那么,有些时候,你还需要写delete
。
或者使用智能指针,比如unique_ptr。这将为您完成工作。
总的来说,一个好的提示是,总是先做一个设计,例如在一张纸上,然后开始编程。
无论如何,请继续努力。