CPP 中过载输入运算符出现分段错误

问题描述 投票:0回答:1

我正在尝试通过提示用户在命令行中依次提供其详细信息来导入一本书。为此,我使用了重载的输入运算符,但在尝试时会导致分段错误。但是,我无法找到问题的根源。

// main.cpp
#include "src/Buch.hpp"
#include "src/Bibliothek.hpp"
#include <iostream>
#include <limits>

void printMenu();

int main()
{

  Bibliothek bookList;

  int choice;
  do {
      printMenu();
      std::cin >> choice;
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Clear input buffer

      switch (choice) {
          case 1: {
              Buch newBook;
              std::cin >> newBook;
              bookList.insertBook(newBook);
              std::cout << "Book added successfully.\n";
              break;
          }
          case 2: {
              std::string ISBN;
              std::cout << "Enter ISBN: ";
              std::getline(std::cin, ISBN);
              Buch* foundBook = bookList.findBookByISBN(ISBN);
              if (foundBook != nullptr) {
                  std::cout << "Book found:\n" << *foundBook << std::endl;
              } else {
                  std::cout << "Book not found.\n";
              }
              break;
          }
          case 3: {
              std::string author;
              std::cout << "Enter author name: ";
              std::getline(std::cin, author);
              Bibliothek booksByAuthor = bookList.getBooksByAuthor(author);
              if (!booksByAuthor.empty()) {
                  std::cout << "Books by " << author << ":\n";
                  booksByAuthor.display();
              } else {
                  std::cout << "No books by " << author << " found.\n";
              }
              break;
          }
          case 4: {
              std::string filename;
              std::cout << "Enter file name to read from: ";
              std::getline(std::cin, filename);
              bookList.readFromFile(filename);
              break;
          }
          case 5: {
              std::string filename;
              std::cout << "Enter file name to save to: ";
              std::getline(std::cin, filename);
              bookList.writeToFile(filename);
              break;
          }
          case 6: {
              bookList.display();
              break;
          }
          case 7: {
              std::cout << "Exiting...\n";
              break;
          }
          default:
              std::cout << "Invalid choice. Please try again.\n";
      }
  } while (choice != 7);

  return 0;
}

void printMenu()
{
    std::cout << "===== Book Management System =====\n";
    std::cout << "1. Add a new book\n";
    std::cout << "2. Search for a book by ISBN\n";
    std::cout << "3. Search for books by author\n";
    std::cout << "4. Read book data from a file\n";
    std::cout << "5. Save book data to a file\n";
    std::cout << "6. Display all books\n";
    std::cout << "7. Exit\n";
    std::cout << "==================================\n";
    std::cout << "Enter your choice: ";
}
// Buch.hpp
#pragma once
#include <string>

class Buch
{
  private:
    std::string author;
    std::string title;
    std::string publisher;
    std::string isbn;
    double price;

  public:
    Buch();
    Buch(std::string author, std::string title, std::string publisher, std::string isbn, double price);
    Buch(const Buch& other);

    std::string getAuthor() const;
    std::string getTitle() const;
    std::string getPublisher() const;
    std::string getISBN() const;
    double getPrice() const;

    friend std::istream& operator>>(std::istream& is, Buch& buch);
    friend std::ostream& operator<<(std::ostream& os, const Buch& buch);
};
// Buch.cpp
#include "Buch.hpp"
#include <iostream>
#include <limits>
#include <ostream>
#include <string>

Buch::Buch() : author(""), title(""), publisher(""), isbn(""), price(0.0) {}

Buch::Buch(std::string author, std::string title, std::string publisher, std::string isbn, double price) : author(author), title(title), publisher(publisher), isbn(isbn), price(price) {}

Buch::Buch(const Buch& other) : author(other.author), title(other.title), publisher(other.publisher), isbn(other.isbn), price(other.price) {}

std::string Buch::getAuthor() const
{
  return author;
}

std::string Buch::getTitle() const
{
  return title;
}

std::string Buch::getPublisher() const
{
  return publisher;
}

std::string Buch::getISBN() const
{
  return isbn;
}

double Buch::getPrice() const
{
  return price;
}

// Input operator
std::istream& operator>>(std::istream& is, Buch& buch) {
    std::cout << "Enter author: ";
    std::getline(is, buch.author);
    std::cout << "Enter title: ";
    std::getline(is, buch.title);
    std::cout << "Enter Publisher: ";
    std::getline(is, buch.publisher);
    std::cout << "Enter ISBN: ";
    std::getline(is, buch.isbn);
    std::cout << "Enter price: ";
    is >> buch.price;
    // Consume the newline character
    is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    return is;
}

// Output operator
std::ostream& operator<<(std::ostream& os, const Buch& buch) {
    os << "Author: " << buch.author << std::endl;
    os << "Title: " << buch.title << std::endl;
    os << "Publisher: " << buch.publisher << std::endl;
    os << "ISBN: " << buch.isbn << std::endl;
    os << "Price: $" << buch.price << std::endl;
    return os;
}
// Bibliothek.hpp
#pragma once
#include "Buch.hpp"

struct Buecherfach
{
  Buch data;
  Buecherfach* prev;
  Buecherfach* next;

  Buecherfach(Buch buch);
};

class Bibliothek
{
  private:
    Buecherfach* head;
    Buecherfach* tail;

  public:
    Bibliothek();

    void push_front(Buch& buch);
    void push_back(Buch& buch);
    Buch front() const;
    Buch back() const;
    void pop_front();
    void pop_back();

    void insertBook(const Buch& buch);
    Buch* findBookByISBN(const std::string& isbn) const;
    Bibliothek getBooksByAuthor(const std::string& author) const;

    bool empty() const;
    void display() const;

    friend std::istream& operator>>(std::istream& is, Bibliothek& list);    
    friend std::ostream& operator<<(std::ostream& os, const Bibliothek& list);

    void writeToFile(const std::string& filename) const;
    void readFromFile(const std::string& filename);
};
// Bibliothek.cpp
#include "Bibliothek.hpp"
#include <iostream>
#include <fstream>

Buecherfach::Buecherfach(Buch buch) : data(buch), prev(nullptr), next(nullptr) {}

Bibliothek::Bibliothek() : head(nullptr), tail(nullptr) {}

void Bibliothek::push_front(Buch& buch) {
    Buecherfach* neuesBuecherfach = new Buecherfach(buch);
    neuesBuecherfach->next = head;  // Set newNode's next pointer to current head
    neuesBuecherfach->prev = nullptr;  // Set newNode's previous pointer to nullptr since it's now the first node
    
    if (head != nullptr) {
        head->prev = neuesBuecherfach;  // Update the previous pointer of the current head to newNode
    }
    
    head = neuesBuecherfach;  // Update head to point to newNode
    if (tail == nullptr) {
        tail = neuesBuecherfach;  // If the list was empty, update tail to newNode
    }
}

void Bibliothek::push_back(Buch& buch) {
    Buecherfach* neuesBuecherfach = new Buecherfach(buch);
    neuesBuecherfach->next = nullptr;  // Since newNode is now the last node, its next pointer is nullptr
    neuesBuecherfach->prev = tail;  // Set newNode's previous pointer to current tail
    
    if (tail != nullptr) {
        tail->next = neuesBuecherfach;  // Update the next pointer of the current tail to newNode
    }
    
    tail = neuesBuecherfach;  // Update tail to point to newNode
    if (head == nullptr) {
        head = neuesBuecherfach;  // If the list was empty, update head to newNode
    }
}

Buch Bibliothek::front() const {
    if (head != nullptr) {
        return head->data;  // Return the value of the head node
    } else {
        throw "List is empty";  // If the list is empty, throw an exception
    }
}

Buch Bibliothek::back() const {
    if (tail != nullptr) {
        return tail->data;  // Return the value of the tail node
    } else {
        throw "List is empty";  // If the list is empty, throw an exception
    }
}

void Bibliothek::pop_front() {
    if (head == nullptr) {
        return;  // If the list is empty, do nothing
    }
    
    Buecherfach* temp = head;  // Store a temporary pointer to the current head
    head = head->next;  // Update head to point to the next node
    if (head != nullptr) {
        head->prev = nullptr;  // Update the previous pointer of the new head to nullptr
    } else {
        tail = nullptr;  // If the list becomes empty, update tail to nullptr
    }
    
    delete temp;  // Delete the node that was removed
}

void Bibliothek::pop_back() {
    if (tail == nullptr) {
        return;  // If the list is empty, do nothing
    }
    
    Buecherfach* temp = tail;  // Store a temporary pointer to the current tail
    tail = tail->prev;  // Update tail to point to the previous node
    if (tail != nullptr) {
        tail->next = nullptr;  // Update the next pointer of the new tail to nullptr
    } else {
        head = nullptr;  // If the list becomes empty, update head to nullptr
    }
    
    delete temp;  // Delete the node that was removed
}

void Bibliothek::insertBook(const Buch& buch) {
    Buecherfach* neuesBuecherfach = new Buecherfach(buch);
    if (head != nullptr) {
        head = tail = neuesBuecherfach;
        return;
    }

    Buecherfach* current = head;
    while (current) {
        if (current->data.getPrice() > buch.getPrice()) {
            if (current == head) {
                neuesBuecherfach->next = head;
                head->prev = neuesBuecherfach;
                head = neuesBuecherfach;
            } else {
                neuesBuecherfach->prev = current->prev;
                neuesBuecherfach->next = current;
                current->prev->next = neuesBuecherfach;
                current->prev = neuesBuecherfach;
            }
            return;
        }
        current = current->next;
    }

    tail->next = neuesBuecherfach;
    neuesBuecherfach->prev = tail;
    tail = neuesBuecherfach;
}

Buch* Bibliothek::findBookByISBN(const std::string& isbn) const {
    Buecherfach* current = head;
    while (current) {
        if (current->data.getISBN() == isbn) {
            return &(current->data);
        }
        current = current->next;
    }
    return nullptr; // Book with given ISBN not found
}

Bibliothek Bibliothek::getBooksByAuthor(const std::string& author) const {
  Bibliothek booksByAuthor;
  Buecherfach* current = head;
  while (current) {
      if (current->data.getAuthor() == author) {
          booksByAuthor.insertBook(current->data);
      }
      current = current->next;
  }
  return booksByAuthor;
}

bool Bibliothek::empty() const {
    return head == nullptr;
}

// Function to display all books in the list
void Bibliothek::display() const {
    Buecherfach* current = head;
    while (current) {
        std::cout << current->data << std::endl;
        current = current->next;
    }
}

void Bibliothek::writeToFile(const std::string& filename) const {
    std::ofstream outFile(filename);
    if (outFile.is_open()) {
        Buecherfach* current = head;
        while (current) {
            outFile << current->data << std::endl;
            outFile << std::endl; // Leave an empty line
            current = current->next;
        }
        outFile.close();
        std::cout << "Data written to file successfully.\n";
    } else {
        std::cerr << "Unable to open file: " << filename << std::endl;
    }
}

void Bibliothek::readFromFile(const std::string& filename) {
    std::ifstream inFile(filename);
    if (inFile.is_open()) {
        Buch book;
        while (inFile >> book) {
            insertBook(book);
        }
        inFile.close();
        std::cout << "Data read from file successfully.\n";
    } else {
        std::cerr << "Unable to open file: " << filename << std::endl;
    }
}

我希望能够成功地将一本书添加到双向链表中,我称之为 Bibliothek(图书馆)。

c++ segmentation-fault operator-overloading doubly-linked-list
1个回答
0
投票

在将每个功能组合到整个程序中之前,您应该检查它们。您的代码包含错误。

例如,考虑来自

main

的代码片段
  switch (choice) {
      case 1: {
          Buch newBook;
          std::cin >> newBook;
          bookList.insertBook(newBook);
          std::cout << "Book added successfully.\n";
          break;
      }
      //...

这条语句可以在空列表

Buch
中插入一个类型为
bookList
的新对象,其中指针
head
tail
都是空指针。

现在让我们研究一下这个函数

insertBook

void Bibliothek::insertBook(const Buch& buch) {
    Buecherfach* neuesBuecherfach = new Buecherfach(buch);
    if (head != nullptr) {
        head = tail = neuesBuecherfach;
        return;
    }

    Buecherfach* current = head;
    while (current) {
        if (current->data.getPrice() > buch.getPrice()) {
            if (current == head) {
                neuesBuecherfach->next = head;
                head->prev = neuesBuecherfach;
                head = neuesBuecherfach;
            } else {
                neuesBuecherfach->prev = current->prev;
                neuesBuecherfach->next = current;
                current->prev->next = neuesBuecherfach;
                current->prev = neuesBuecherfach;
            }
            return;
        }
        current = current->next;
    }

    tail->next = neuesBuecherfach;
    neuesBuecherfach->prev = tail;
    tail = neuesBuecherfach;
}

对于初学者来说,这个 if 语句

if (head != nullptr) {
    head = tail = neuesBuecherfach;
    return;
}

没有意义。看来你的意思是

if (head == nullptr) {
        ^^^^  
    head = tail = neuesBuecherfach;
    return;
}

如果要返回到原始代码,则对于空列表将跳过此 if 语句。并且控制权会被传递给下面的代码

    Buecherfach* current = head;
    while (current) {
        if (current->data.getPrice() > buch.getPrice()) {
            if (current == head) {
                neuesBuecherfach->next = head;
                head->prev = neuesBuecherfach;
                head = neuesBuecherfach;
            } else {
                neuesBuecherfach->prev = current->prev;
                neuesBuecherfach->next = current;
                current->prev->next = neuesBuecherfach;
                current->prev = neuesBuecherfach;
            }
            return;
        }
        current = current->next;
    }

由于列表为空,因此 while 循环也将被跳过。在循环之后,您尝试使用空指针访问内存

tail->next = neuesBuecherfach;

这会导致未定义的行为。

我认为你的程序还有其他错误。

© www.soinside.com 2019 - 2024. All rights reserved.