簡體   English   中英

對 object 指針向量進行排序和使用二進制搜索

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM