我需要构建一个服装项目(Line)的双向链表(LinkedList)。我们的想法是提供类似于列表的功能。每个类都可以自己工作,但是当我尝试将“数据”成员变量的数据类型从LinkedList中的字符串更改为Line时,所有地狱都会破坏,我主要得到关于“数据”的错误:未定义类,未定义类型,不完整类型等...
我很感激任何帮助,因为我的头已经开始受伤了!
int main() {
string node1{ "1" };
string node2{ "2" };
string node3{ "3" };
LinkedList list;
cout << "Is Empty? " << list.empty() << endl;
cout << "Size " << list.size();
list.push_front(node3);
list.push_front(node2);
list.push_front(node1);
cout << "Is Empty? " << list.empty() << endl;
cout << "Size " << list.size() << "\n\n";
for (int i{ 1 }; i <= list.size(); ++i) {
list.print(i);
}
cout << endl;
return 0;
}
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include "Line.h"
#include <string>
using T = std::string;
class LinkedList
{
public:
LinkedList() : theSize{ NULL }, head{ nullptr }, tail{ nullptr } {}; // default ctor
virtual ~LinkedList(); // dtor
LinkedList(const LinkedList&); // copy-ctor
const LinkedList& operator=(const LinkedList&); // copy assignment operator
void push_front(const T&); // insert node at front of list
void push_back(const T&); // insert node at back of list
void pop_front(); // remove the first node from front of the list
void pop_back(); // remove the last node from the end of the list
void insert(const T&, int); // insert a new node with T at position k in the list
void remove(int); // remove node at position k in the list
int size() const; // returns the size of this list
bool empty() const; // returns whether this list is empty
void print(int) const; // print the contents at position
private:
class ListNode {
public:
ListNode() : nextPtr{ nullptr }, prevPtr{ nullptr } {}; // default ctor
explicit ListNode(const T&, ListNode* = nullptr, ListNode* = nullptr); // ctor
~ListNode() = default; // dtor
const T& get_data() const { // get data in node
return data;
}
ListNode* get_nextPtr() const { // get the pointer to the next node
return nextPtr;
}
ListNode* get_prevPtr() const { // get the pointer to previous node
return prevPtr;
}
void set_data(const T& data) { // set data in node
this->data = data;
}
void set_nextPtr(ListNode* nextPtr) { // set the next pointer in node
this->nextPtr = nextPtr;
}
void set_prevPtr(ListNode* prevPtr) { // set the previous pointer in node
this->prevPtr = prevPtr;
}
private:
T data; // node data member
ListNode* nextPtr; // pointer to the next node
ListNode* prevPtr; // pointer to the previous node
};
int theSize;
ListNode* head;
ListNode* tail;
// utility function to allocate new node
ListNode* getNewNode(const T& data) {
return new ListNode{ data };
}
// utility function to deallocate list
void clear();
// utility function to copy list contents
void copy(const LinkedList& rhs);
};
#endif
#include "stdafx.h"
#include "LinkedList.h"
#include <string>
#include <iostream>
using std::cout;
using std::endl;
// virtual dtor for LinkedList class
LinkedList::~LinkedList() {
this->clear();
}
// copy-ctor
LinkedList::LinkedList(const LinkedList& rhs) {
this->copy(rhs);
}
// copy assignment operator
const LinkedList& LinkedList::operator=(const LinkedList& rhs) {
if (&rhs != this) { // avoid self-assignment
this->clear(); // release space
this->copy(rhs); // copy contents of rhs into new object
}
// enable cascading calls such as x = y = z
return *this;
}
// insert node at front of list
void LinkedList::push_front(const T& data) {
ListNode* newPtr{ getNewNode(data) }; // new node
if (empty()) { // list is empty
head = tail = newPtr; // both head and tail point to the only existing node
}
else {
head->set_prevPtr(newPtr); // aim the previous pointer of the old head node at the new head node
newPtr->set_nextPtr(head); // aim new head node to old head node
head = newPtr; // aim head at the new node
}
++theSize; // increment the size of list
}
// insert node at back of list
void LinkedList::push_back(const T& data) {
ListNode* newPtr{ getNewNode(data) }; // new node
if (empty()) { // list is empty
head = tail = newPtr; // both head and tail point to the only existing node
}
else {
tail->set_nextPtr(newPtr); // aim next pointer of the old tail node to the new tail node
newPtr->set_prevPtr(tail); // aim previous pointer of new tail node to the old tail node
tail = newPtr; // aim tail at the new tail node
}
++theSize; // increment the size of list
}
// remove the first node from front of the list
void LinkedList::pop_front() {
if (empty()) { // list is empty
cout << "List is empty, unable to remove from an empty list!\n";
}
else {
ListNode* tempPtr{ head }; // hold item to delete
if (head == tail) { // at most one node in list
head = tail = nullptr; // no nodes remain after removal
}
else {
head = head->get_nextPtr(); // set the head to point to the previous second node
head->set_prevPtr(nullptr); // the previous pointer of head must be nullptr
}
delete tempPtr; // delete the old first item
tempPtr = nullptr;
--theSize; // decrement the size of list
}
}
// remove the last node from the end of the list
void LinkedList::pop_back() {
if (empty()) { // list is empty
cout << "List is empty, unable to remove from an empty list!\n";
}
else {
ListNode* tempPtr{ tail }; // hold item to delete
if (head == tail) { // at most one node in list
head = tail = nullptr; // no nodes remain after removal
}
else {
ListNode* currentPtr{ head };
while (currentPtr->get_nextPtr() != tail) {
currentPtr = currentPtr->get_nextPtr();
}
tail = currentPtr;
currentPtr->set_nextPtr(nullptr); // the nextPtr of the tail must be null
}
delete tempPtr; // delete the old last item
tempPtr = nullptr;
--theSize; // decrement the size of list
}
}
// insert a new node at position k in the list
void LinkedList::insert(const T& data, int position) {
if (position == 1) {
push_front(data); // if position is one, use the push_front function to add node to the front of the list
}
else if (position > 1 && position <= theSize) {
ListNode* newPtr{ getNewNode(data) };
ListNode* currentPtr{ head };
for (int i { 1 }; i <= position - 1; ++i) {
currentPtr = currentPtr->get_nextPtr(); // traverse the list until location at (position - 1)
}
newPtr->set_prevPtr(currentPtr->get_prevPtr()); // aim the prevPtr of newPtr to node at current location at (position - 1)
newPtr->set_nextPtr(currentPtr); // aim the nextPtr of newPtr to node at old location at position
currentPtr->get_prevPtr()->set_nextPtr(newPtr); // aim the nextPtr of node at location (position - 1) at new node newPtr
currentPtr->set_prevPtr(newPtr); // aim the prevPtr of the node at old location at position at point at the new node newPtr
++theSize; // increment the size of list
}
else {
cout << "Position is out of bounds. Position must be in [" << 1 << "," << theSize << "].\n";
}
}
// remove node at position k in the list
void LinkedList::remove(int position) {
if (position == 1) {
pop_front(); // if position is one, use the pop_front function to remove node from the front of the list
}
else if (position == theSize) {
pop_back(); // if position is at the end of the list, use the pop_back function to remove node from the back of the list
}
else if (position > 1 && position < theSize) {
ListNode* tempPtr{ head };
for (int i{ 1 }; i < position; ++i) {
tempPtr = tempPtr->get_nextPtr(); // traverse the list until location at position
}
tempPtr->get_prevPtr()->set_nextPtr(tempPtr->get_nextPtr()); // aim the nextPtr of node at old location at (position - 1) to node at old location (position + 1)
tempPtr->get_nextPtr()->set_prevPtr(tempPtr->get_prevPtr()); // aim the prevPtr of node at old location at (position + 1) to node at old location (position - 1)
delete tempPtr; // remove node and free space
tempPtr = nullptr;
--theSize; // decrement the size of list
}
else {
cout << "Position is out of bounds. Position must be in [" << 1 << "," << theSize << "].\n";
}
}
// returns the size of this list
int LinkedList::size() const {
return theSize;
}
// returns whether this list is empty
bool LinkedList::empty() const {
return theSize == 0;
}
// prints contents of the list at location at position
void LinkedList::print(int position) const {
if (!empty()) {
if (position >= 1 && position <= theSize) {
ListNode* tempPtr{ head };
for (int i{ 1 }; i <= position - 1; ++i) {
tempPtr = tempPtr->get_nextPtr(); // traverse the list until location at position
}
cout << tempPtr->get_data() << endl;
}
else {
cout << "Position is out of bounds. Position must be in [" << 1 << "," << theSize << "].\n";
}
}
}
// utility function to deallocate list
void LinkedList::clear() {
if (!empty()) { // list is not empty
ListNode* currentPtr{ head };
ListNode* tempPtr{ nullptr };
while (currentPtr != nullptr) { // traverse the list and delete remaining nodes
tempPtr = currentPtr;
currentPtr = currentPtr->get_nextPtr();
delete tempPtr;
--theSize;
}
head = tail = nullptr;
}
}
// utility function to copy list contents
void LinkedList::copy(const LinkedList& rhs) {
ListNode* tempPtr{ rhs.head };
// traverse the list and insert data in node at each corresponding position
while (tempPtr != nullptr) {
push_back(tempPtr->get_data());
tempPtr = tempPtr->get_nextPtr();
}
}
// ctor for ListNode class
LinkedList::ListNode::ListNode(const T& data, ListNode* prevPtr, ListNode* nextPtr) {
set_data(data);
set_nextPtr(nextPtr);
set_prevPtr(prevPtr);
}
#pragma once
#include <iostream>
class Line
{
public:
Line() : linePtr{ new char {NULL} }, lineLength{ 1 }, lineCapacity{ 0 } {}; // default ctor
Line(const char*); // ctor: construct this line from a given C-string
Line(char); // ctor: construct this line from a given character
virtual ~Line(); // virtual dtor
Line(const Line&); // copy-ctor
const Line& operator=(const Line&); // copy-assignment operator
const char* cstr() const; // return C-style version of this line
int length() const; // return length of this line
int capacity() const; // return capacity of this line
void resize(); // double capacity if this line is full
bool empty() const; // return whether this line is empty
bool full() const; // return whether this line is full
void push_back(const char&); // append character to the end of this line
void pop_back(); // remove the last character in this line
friend std::ostream& operator<<(std::ostream&, const Line&); // print this line
friend std::istream& operator>>(std::istream&, Line&); // reads into this line
private:
char* linePtr; // pointer to the first character in this line
int lineLength; // length of this line
int lineCapacity; // storage capacity of the line
};
// definitions of public member functions and utility functions of the Line class.
// definitions of friend overloaded input and output operators
#include "stdafx.h"
#include "Line.h"
#include <cstring>
using namespace std;
// ctor: construct this line from a given C-string
Line::Line(const char* cstring) {
lineLength = strlen(cstring) + 1;
lineCapacity = lineLength - 1;
linePtr = new char[lineCapacity + 1] {}; // create a new character pointer of appropriate size for the line object
strncpy_s(linePtr, lineLength, cstring, lineLength); // copy cstring into line with strncpy_s instead of strncpy due to Visual Studio Error with using strncpy
}
// ctor: construct this line from a given character
Line::Line(char ch) : linePtr{ new char[2] { ch, NULL } }, lineLength{ 2 }, lineCapacity{ 1 } {
/* empty body */
}
// virtual dtor
Line::~Line() {
delete[] linePtr; // release line space
// reset member data
linePtr = nullptr;
lineCapacity = 0;
lineLength = 1;
}
// copy-ctor
Line::Line(const Line& rhs) : linePtr{ new char[rhs.lineCapacity + 1] {} },
lineLength {rhs.lineLength},
lineCapacity{rhs.lineCapacity} {
strncpy_s(this->linePtr, this->lineLength, rhs.linePtr, rhs.lineLength); // strncpy_s instead of strncpy due to Visual Studio Error with using strncpy
}
// copy-assignment operator
const Line& Line::operator=(const Line& rhs) {
if (&rhs != this) { // avoid self-assignment
// if the two sides are of different lengths or capacities;
// deallocate the lhs line, then allocate new lhs line
if (this->lineCapacity != rhs.lineCapacity || this->lineLength != rhs.lineLength) {
delete[] linePtr; // deallocate lhs line
this->lineCapacity = rhs.lineCapacity;
this->lineLength = rhs.lineLength;
this->linePtr = new char[rhs.lineCapacity + 1] {};
}
strncpy_s(this->linePtr, this->lineLength, rhs.linePtr, rhs.lineLength);
}
return *this;
}
// return C-style version of this line
const char* Line::cstr() const {
return linePtr;
}
// return length of this line
int Line::length() const {
return lineLength;
}
// return capacity of this line
int Line::capacity() const {
return lineCapacity;
}
// double capacity of line in case line is full
void Line::resize() {
if (full()) {
char* tempPtr{ new char[lineCapacity + 1] {} }; // data holder to prevent loss of data
strncpy_s(tempPtr, lineLength, linePtr, lineLength); // strncpy_s instead of strncpy due to Visual Studio Error with using strncpy
delete[] linePtr; // deallocate the line;
// double capacity
if (lineCapacity == 0) {
lineCapacity = 1;
}
else {
lineCapacity *= 2;
}
linePtr = new char[lineCapacity + 1] {}; // create a new line with double capacity
strncpy_s(linePtr, lineLength, tempPtr, lineLength);
delete[] tempPtr; // deallocate the temporary data holder
tempPtr = nullptr;
}
}
// return whether this line is empty
bool Line::empty() const {
return (lineLength == 1 && lineCapacity >= lineLength); // null terminating character at the beginning of line
}
// return whether this line is full
bool Line::full() const {
return lineCapacity == (lineLength - 1); // only occurs when terminating NULL character at end of line;
}
// append character to the end of this line
// appending a NULL character has no effect
void Line::push_back(const char& ch) {
if (ch != NULL) {
resize();
++lineLength;
linePtr[lineLength - 2] = ch;
linePtr[lineLength - 1] = NULL;
}
}
// remove the last character in this line
void Line::pop_back() {
if (!empty()) {
linePtr[lineLength - 2] = NULL;
--lineLength;
}
else {
cout << "Line is empty. Nothing to remove.\n";
}
}
// print this line
ostream& operator<<(ostream& output, const Line& line) {
output << line.cstr() << endl;
return output; // enables cascading: cout << x << y
}
// reads into this line
istream& operator>>(istream& input, Line& line) {
char ch{ NULL };
cin >> noskipws; // read-in whitespaces
while (ch != '\n') {
input >> ch;
line.push_back(ch);
}
return input; // enables cascading: cin >> x >> y
}