I am trying to make a queue implementing a linked list but am running into a compiler error. The error is coming from the overloaded assignment operator function on the line where I call the destructor (marked with an all-caps comment). I have a hunch it is a simple fix that has something to do with the syntax of my constructor/destructor declarations.
The error I am getting states the following code: error C2512: 'Queue<char>::Queue' : no appropriate default constructor available
It mentions no constructor, but the line it refers to is the one below where I am trying to call the destructor.
Thanks in advance for your help.
#ifndef QUEUE_H
#define QUEUE_H
#include <iostream>
using namespace std;
template <class Type>
class Queue // Create a Queue data structure implementing a linked list
{
private: // The private members
struct Cell // The Cell class will be the blueprints for each link in the list
{
Type data; // The information held by the cell
Cell* next; // The link to the next cell
};
Cell* first = NULL;
Cell* last = NULL;
public: // The public members
Queue(Type);
bool isEmpty();
void push(Type);
Type pop();
Queue<Type>& operator=(Queue<Type>&);
friend ostream& operator<<(ostream&, const Queue<Type>&);
~Queue();
};
template<class Type>
Queue<Type>::Queue(Type inputData) // Constructor that initializes the queue with a new cell that last and first point to
{
first = new Cell;
first->data = inputData;
first->next = NULL;
last = first;
}
template<class Type>
Queue<Type>& Queue<Type>::operator=(Queue<Type>& queue) // Overload "=" so that it performs a deep copy of a Queue object
{
if (!queue.isEmpty())
{
~Queue(); // HERE IS THE ERROR LINE
Cell* rhs = queue.first;
while (rhs != NULL)
{
push(rhs->data);
rhs = rhs->next;
}
}
return *this;
}
template<class Type>
Queue<Type>::~Queue() // Destructor that deallocates all of the memory used by the queue.
{
if (!isEmpty()) // We only need to deallocate the queue if it is non-empty
{
Cell *link = last;
while (link != NULL) // Until we reach the end of the queue, keep deleting each link
{
pop();
}
first = NULL;
last = NULL;
}
else // If the queue is already empty, let the user know
{
cout << "Cannot call destructor. The list is already empty.\n";
}
}
#endif
Check out this thread: Can i call destructor from its class method?. An easy way around this is to make a function to empty the queue, then call it from the destructor and assignment operator.
template<class Type>
void Queue<Type> empty(){
if (!isEmpty()) // We only need to deallocate the queue if it is non-empty
{
Cell *link = last;
while (link != NULL) // Until we reach the end of the queue, keep deleting each link
{
pop();
}
first = NULL;
last = NULL;
}
else // If the queue is already empty, let the user know
{
cout << "Cannot call empty. The list is already empty.\n";
}
}
template<class Type>
Queue<Type>& Queue<Type>::operator=(Queue<Type>& queue) // Overload "=" so that it performs a deep copy of a Queue object
{
if (!queue.isEmpty())
{
empty(); // Tada, no more error
Cell* rhs = queue.first;
while (rhs != NULL)
{
push(rhs->data);
rhs = rhs->next;
}
}
return *this;
}
template<class Type>
Queue<Type>::~Queue() // Deconstructor that deallocates all of the memory used by the queue.
{
empty();
}
This has nothing to do with template.
If you declare any constructor for your class, the compiler synthesized default constructor(i.e. the one that takes no arg) is deleted.
You have to define Queue() yourself.
BTW, a using directive in the global scope is not a good idea.
I guess you define a queue without parameter, like
Queue<char> quCh;
If you want to do this, you must define a constructor without parameter.
Queue();
or you must define your queue like this:
Queue<char> quCh('a');
Related
I'm having trouble switching this linked stack file into using only smart pointers. I've tried some things and I just get loads of errors. Any help is appreciated. This is in C++ by the way.
There is supposed to be no raw pointers, no constructors not needed, and use of the auto keyword whenever possible.
Here's the code:
#include <new>
#include <string>
#include "PrecondViolatedExcep.h"
template <typename ItemType>
LinkedStack<ItemType>::LinkedStack(const LinkedStack<ItemType>& aStack) {
if (!aStack.topPtr) {
topPtr = nullptr;
}
else {
NodePtr origStackPtr(aStack.topPtr);
try {
topPtr = new Node<ItemType>(origStackPtr->getItem() );
NodePtr newStackPtr(topPtr);
origStackPtr = origStackPtr->getNext();
while (origStackPtr) {
newStackPtr->setNext(
new Node<ItemType>(origStackPtr->getItem())
);
newStackPtr = newStackPtr->getNext();
origStackPtr = origStackPtr->getNext();
}
}
catch (const std::bad_alloc&) {
while (!isEmpty() ) {
pop();
}
throw;
}
}
}
template <typename ItemType>
LinkedStack<ItemType>::~LinkedStack() {
while (!isEmpty() ) {
pop();
}
}
template <typename ItemType>
bool LinkedStack<ItemType>::isEmpty() const {
return !topPtr;
}
template <typename ItemType>
bool LinkedStack<ItemType>::push(const ItemType& newItem) {
try {
topPtr = new Node<ItemType>(newItem,
topPtr);
}
catch (const std::bad_alloc&) {
return false;
}
return true;
}
template <typename ItemType>
bool LinkedStack<ItemType>::pop() {
if(!isEmpty() ) {
NodePtr nodeToDeletePtr(topPtr);
topPtr = topPtr->getNext();
delete nodeToDeletePtr;
return true;
}
return false;
}
template <typename ItemType>
ItemType LinkedStack<ItemType>::peek() const {
// throw(PrecondViolatedExcep) {
if (isEmpty() ) {
std::string message("LinkedStack::peek() ");
message += "called on an empty stack.";
throw PrecondViolatedExcep(message);
}
return topPtr->getItem();
}
I hope the commentary on each piece of the answer demonstrates the principles well enough you can learn more than just these specific solutions.
Though you don't show the definitions of the class templates, it looks like LinkedStack<ItemType> has a private member topPtr, and you'd like to change its type from Node<ItemType>* to std::unique_ptr<Node<ItemType>>. Also, class template Node has member functions something like:
template <class ItemType>
class Node
{
// ...
// Either public, or Node befriends LinkedStack in some way.
explicit Node(const ItemType& value, Node* next = nullptr);
Node* getNext() const { return nextPtr; }
void setNext(Node* ptr) { nextPtr = ptr; }
// ...
// Probably private:
Node* nextPtr;
};
Changes to this Node template could look like:
template <class ItemType>
class Node
{
// ...
// Either public, or Node befriends LinkedStack in some way.
explicit Node(const ItemType& value)
Node(const ItemType& value, std::unique_ptr<Node> next)
: Node(value) { nextPtr = std::move(next); }
Node* getNext() const { return nextPtr.get(); }
void setNext(std::unique_ptr<Node> ptr) { nextPtr = std::move(ptr); }
void resetNext() { nextPtr = nullptr; }
[[nodiscard]] std::unique_ptr<Node> releaseNext()
{ return std::move(nextPtr); }
// ...
// Probably private:
std::unique_ptr<Node> nextPtr;
};
The member becomes a smart pointer because the Node "owns" the responsibility for cleaning up its "next" node, if any.
The pointer parameters of the two-argument constructor and setNext become smart pointers because calling each means the Node will take over responsibility for cleaning up that next Node. They need to use std::move to allow the member to actually take that responsibility from the parameter.
You might ask, shouldn't getNext return a smart pointer? No, because that would mean that calling getNext transfers responsibility for cleaning up the next node to the caller, and that's usually not what getNext is for. A raw pointer can still have its place, meaning a pointer to an object whose ownership is handled by something else, or possibly a null pointer. (When null isn't a possibility, we'd often consider changing from a raw pointer to a reference, which also implies ownership is handled by something else.)
Though for cases where we do want to take ownership back from a Node, I've added releaseNext(). It returns a unique_ptr to "give away" its responsibility, and in the process its own unique_ptr member becomes empty.
Finally, I've added resetNext as a way to reset the nextPtr back to null, more straightforward than passing an empty smart pointer to setNext. (And/or, we could overload a setNext(std::nullptr_t).)
Then Node can follow the Rule of Zero: It does not need to declare a destructor, copy constructor, move constructor, or any sort of assignment operator. Since it has a member of type std::unique_ptr<Node>, its implicitly-declared copy constructor and copy assignment members will be defined as deleted, meaning the compiler will complain about any code that tries to use them. But it will have working move constructor and move assignment operator, which are probably not needed but harmless.
Now to class template LinkedStack. It can't use the Rule of Zero since copying it should be allowed despite the unique_ptr member and should do a deep copy. So we'll go with the Rule of Five, as modified by the Copy and Swap Idiom:
template <class ItemType>
class LinkedStack
{
public:
LinkedStack() = default;
LinkedStack(const LinkedStack&);
LinkedStack(LinkedStack&&) = default;
LinkedStack& operator=(LinkedStack) noexcept;
~LinkedStack() = default;
friend void swap(LinkedStack& s1, LinkedStack& s2) {
using std::swap;
swap(s1.topPtr, s2.topPtr);
}
// ...
private:
std::unique_ptr<Node<ItemType>> topPtr;
};
The destructor is okay to default, so with the = default; above, you can delete your custom definition.
Assignment is defined per Copy And Swap:
template <class ItemType>
LinkedStack<ItemType>& LinkedStack<ItemType>::operator=(
LinkedStack rhs) noexcept
{
swap(*this, rhs);
return *this;
}
The copy constructor gets simpler:
template <typename ItemType>
LinkedStack<ItemType>::LinkedStack(const LinkedStack<ItemType>& aStack)
: topPtr() // initially null
{
if (aStack.topPtr) {
Node* origStackPtr = aStack.get();
topPtr = std::make_unique<ItemType>(origStackPtr->getItem());
Node* newStackPtr(topPtr.get());
origStackPtr = origStackPtr->getNext();
while (origStackPtr) {
newStackPtr->setNext(
std::make_unique<ItemType>(origStackPtr->getItem())
);
newStackPtr = newStackPtr->getNext();
origStackPtr = origStackPtr->getNext();
}
}
}
The loop uses raw Node* pointers because once the nodes are safely stored in the topPtr or in another Node, the constructor code no longer needs to worry about deleting them. But it does need to reassign the variables as the loop executes, so references won't do, and it also needs to detect when origStackPtr->getNext() returns a null pointer.
Your original needed the try-catch-rethrow because in case of an exception in a constructor body, the destructor for that class is not called. But destructors of its members and base classes are called. So now if an exception happens in the copy constructor body, the destructor for the unique_ptr member topPtr executes, which will take care of deleting the top Node. Destruction of that Node's unique_ptr member will likewise delete its next node if any, and so on recursively - all with zero lines of (your) code.
isEmpty does not need any change: the expression !topPtr also works with a unique_ptr, meaning "is not null".
A straightforward update of push just changes the new to a make_unique:
template <typename ItemType>
bool LinkedStack<ItemType>::push(const ItemType& newItem) {
try {
topPtr = std::make_unique<Node<ItemType>>(
newItem, topPtr);
}
catch (const std::bad_alloc&) {
return false;
}
return true;
}
However, I'd recommend getting rid of the try/catch and return value. A memory error is rare, so most code shouldn't be checking to see whether this push called one. Something that really does care ought to do a try/catch itself around whatever amount of code is most appropriate. This would reduce the body to just one statement.
And pop gets simpler:
template <typename ItemType>
bool LinkedStack<ItemType>::pop() {
if (!isEmpty()) {
topPtr = topPtr->releaseNext();
return true;
}
return false;
}
Notice in the topPtr reassignment statement, first releaseNext() is used to take responsibility for the second Node (if any) away from the top Node. Then that responsibility is immediately given to the topPtr variable by the assignment. This also means topPtr will delete what it previously pointed at, the top node being popped off. Since that top node no longer has a next node, nothing else gets deleted, and the stack ends up in the correct state just like in the old version.
peek() does not require any change. The topPtr->getItem() expression will work via the unique_ptr<T>::operator-> function. (But I'd suggest changing the return types of Node<ItemType>::getItem() and LinkedStack<ItemType>::peek() to const ItemType&, to avoid useless copies when ItemType is a class type.)
Complete repository: https://github.com/mervynlee94/queue/tree/master
I have implemented LinkedList and tested my copy constructor without issue.
LinkedList<int> l1;
l1.insert(1,3); // set value 3 in 1st position
l1.insert(2,7); // set value 7 in 2nd position
l1.insert(3,9); // set value 9 in 3rd position
LinkedList<int> l2(l1); // copy constructor
l2.setEntry(2,5); // set value 5 in position 2
cout<< l1.getEntry(2) <<endl; // output: 7
cout<< l2.getEntry(2) <<endl; // output: 5
My Queue implementation uses this LinkedList module as the data structure with code below.
#ifndef _LIST_QUEUE
#define _LIST_QUEUE
#include "QueueInterface.h"
#include "LinkedList.h"
template<class ItemType>
class ListQueue : public QueueInterface<ItemType> {
private :
LinkedList<ItemType> list;
public :
ListQueue();
ListQueue(const ListQueue& aQueue);
~ListQueue();
bool isEmpty() const ;
void enqueue( const ItemType& newEntry);
void dequeue();
ItemType peekFront() const;
};
#endif
template<class ItemType>
ListQueue<ItemType>::ListQueue() {
}
template<class ItemType>
ListQueue<ItemType>::ListQueue(const ListQueue& aQueue) {
list = LinkedList<ItemType>(aQueue.list);
}
template<class ItemType>
ListQueue<ItemType>::~ListQueue() {}
template<class ItemType>
bool ListQueue<ItemType>::isEmpty() const {
return list.isEmpty();
}
template<class ItemType>
void ListQueue<ItemType>::enqueue(const ItemType& newEntry) {
list.insert(list.getLength()+1, newEntry);
}
template<class ItemType>
void ListQueue<ItemType>::dequeue() {
list.remove(1);
}
template<class ItemType>
ItemType ListQueue<ItemType>::peekFront() const {
return list.getEntry(1);
}
When I test the code out in main.cpp, I encountered segmentation error.
ListQueue<int> q1;
q1.enqueue(1);
q1.enqueue(2);
q1.enqueue(3);
ListQueue<int> q2(q1); // Set breakpoint here
q2.enqueue(5);
cout<<q2.peekFront()<<endl;
cout<<q1.peekFront()<<endl;
I set a debugging breakpoint on above and I realise one thing. The copy constructor in Queue implementation doesn't work properly.
The debugging value shown in copy constructor of aQueue.list and this.list varies.
Here is the LinkedList copy constructor implementation
template<class ItemType>
LinkedList<ItemType>::LinkedList(const LinkedList<ItemType>& aList) {
Node<ItemType>* listPtr = aList.headPtr;
if(listPtr == nullptr) {
headPtr = nullptr;
}
else {
headPtr = new Node<ItemType>(listPtr->getItem());
Node<ItemType>* newPtr = headPtr;
while(listPtr->getNext() != nullptr) {
listPtr = listPtr->getNext();
Node<ItemType>* newNode = new Node<ItemType>(listPtr->getItem());
newPtr->setNext(newNode);
newPtr = newPtr->getNext();
}
}
itemCount = aList.getLength();
}
Note that since the copy-ctor of LinkedList is already implemented, you don't have to reimplement it - C++ will do it automatically for you.
Another important thing - copy constructor and copy assignment operator are distict.
You implemented Copy-ctor for your Queue, and in this copy-ctor you used the copy-assignment operator, and I suspect you did not implement it.
The easiest way to fix it is to simply remove the copy constructor and destructor, they are not necessary. The correct way to invoke a copy-constructor is as follows:
template<class ItemType>
ListQueue<ItemType>::ListQueue(const ListQueue& aQueue)
: list(aQuquq.list) {
}
I have three more points to make:
Rule of 3 - if you declare one of copy-constructor, copy-assignment or destructor, you almost surely have to implement them all.
There is a bit more complicated feature in C++ called "move semantics", in case you are familiar with the term, you should consider the Rule of 5 - the same as the previous rule, with addition of move-constructor, and move-assignment operator.
It seems as you are using inheritance, but I don't see the keyword override anywhere. If you are using C++11 or above, consider adding the override qualifier to overriding methods.
And, lastly, C++11 supports default ctors/dtors and assignment operators. If you write an empty destructor (as you do here), the cleaner syntax is to write
class ListQueue : public QueueInterface<ItemType> {
...
~ListQueue() = default;
...
};
I have to implement a Circularly linked queue class as the LinkedQueueType class. For some reason, when I call the enqueue function, it isn't getting added to the queue, even my test prints don't print out. Here is my queue class:
#define LINKED_QUEUE_H
class FullQueue {};
class EmptyQueue{};
typedef int ItemType;
struct NodeType{
ItemType info;
NodeType* next;
};
class LinkedQueueType {
public:
LinkedQueueType ();
// Class constructor.
// Because there is a default constructor, the precondition // that the queue has been initialized is omitted. LinkedQueueType(const LinkedQueueType& qt);
//Copy Constructor
LinkedQueueType operator=(const LinkedQueueType& rhs); //Overloaded assignment operator=
~LinkedQueueType ();
// Class destructor.
void MakeEmpty();
// Function: Initializes the queue to an empty state.
// Post: Queue is empty.
bool IsEmpty() const;
// Function: Determines whether the queue is empty.
// Post: Function value = (queue is empty)
bool IsFull() const;
// Function: Determines whether the queue is full. // Post: Function value = (queue is full)
void Enqueue(ItemType newItem);
// Function: Adds newItem to the rear of the queue. // Post: newItem is at rear of queue.
void Dequeue(ItemType& item);
// Function: Removes front item from the queue and returns it in // item.
// Post: If (queue is empty) EmptyQueue exception is thrown
// and item is undefined
// else front element has been removed from queue and
// item is a copy of removed element.
void Print(); //Print function
private:
NodeType* rear;
int length;
};
#endif
and here is the implementation for the enqueue function:
void LinkedQueueType::Enqueue(ItemType newItem){
NodeType *newNode=nullptr;
NodeType*temp=nullptr;
newNode->info=newItem;
newNode->next=nullptr;
if(rear==nullptr){
rear=newNode;
}else{
temp=rear->next;
rear->next=newNode;
}
rear=newNode;
rear->next=temp;
}
Here is my testing:
cout<<"hi";
LinkedQueueType q;
q.Enqueue(5);
When I run my driver, it doesn't print, could someone show me the way!
This is your code:
NodeType *newNode=nullptr;
NodeType*temp=nullptr;
newNode->info=newItem;
newNode->next=nullptr;
This is what you are saying:
Make a new pointer called newNode of type NodeType, and let it point to nothing.
Do the same for variable 'temp'.
Now we put newItem in nothing -> Because that is what newNode is pointing to.
Your program tries to dereference the variable newNode to access its property info. But that's not possible.
This is what your code should be:
NodeType *newNode = new NodeType();
NodeType*temp=nullptr;
newNode->info=newItem;
newNode->next=nullptr;
This way your program has an object to derefer and get a property from.
The code you posted definitely crashes (segmentation fault on Linux or access violation on Windows™) dereferencing the null pointer. You need to create the node before accessing it, like:
NodeType *newNode = new NodeType();
I'm reading lines from a file and storing them into linked list.
void add(llist *list, somevalue) {
Node *newnode = (Node *) malloc(sizeof(Node));
newnode->value = somevalue;
newnode->next = list->head;
list->head = newnode;
}
and I call this function from an initialize function which opens the file and reads lines from the file.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
And another problem that I realized is the values get concatenated every time I try to print the value. That is to say, the output would be:
First Loop: Tony
Second Loop: Peter
TonyPeter
Third Loop: Mir
PeterMir
TonyPeterMir
How do I fix it so the add function permanently adds the node to the linked list?
Why would the values be jumbled up like that?
----EDITED----
The list is a global variable, and here are some more snippets from the init function. This is the while loop with the problem:
//open file
//initialize & declare pointers
while (1) {
for (i = 0; i < max; i++) {
value[i] = '\0';
}
if (!(fgets(value,max,f))) {
//segfaults if I try to print out the list inside this block.
break;
}
add(list, value);
//the values are well separated in this statement
printf("id is %s\n", list->head->value);
//This print list works, but works weird as shown above.
print_list(list);
}
fclose(f);
//This print list doesn't work, the list is NULL
print_list(list);
And this is the print list function:
void print_users(llist *list) {
ListNode *e;
if (list->head == NULL) {
printf("NO USERS\r\n");
return;
}
e = list->head;
while (e != NULL) {
puts(e->id);
e = e->next;
}
}
So I don't have a good grasp at all on what you're exactly trying to do here, so we can only do but so much. You may consider posting a MCVE. However, I may be able to give you some pointers on building a linked list. I directly copied your add function into a linked list class that I just hurriedly built, and it worked fine, so there may be something else in your llist class that is causing the issue, or it could be something else in your code. The class and a brief description of the class are listed below.
basic node class
Note: I used templates, but you could just as easily remove the template statements and replace T with any type.
template<typename T>
class node {
private:
T data;
node* next;
public:
// The C++11 rule of 5
// Default Constructor
node(T value = T(), node* nxt = nullptr) : data(value), next(nxt) { }
// Copy Constructor
node(const node<T>& src) : data(src.data), next(src.next) { }
// Move Constructor
node(node<T>&& src) : data(src.data), next(src.next) {
src.data = T();
src.next = nullptr;
}
// Copy Assignment Operator
node<T>& operator=(const node<T>& src) {
this->data = src.data;
this->next = src.next;
return(*this);
}
// Move Assignment Operator
node<T>& operator=(node<T>&& src) {
this->data = src.data;
this->next = src.next;
src.data = T();
src.next = nullptr;
}
// Destructor
~node() {};
// Some functions to help with encapsulation
void set_next(node* nxt) {
this->next = nxt;
}
void set_data(T value) {
this->data = value;
}
node* get_next() {
return(this->next);
}
T& get_data() {
return(data);
}
};
linked list class body
Since you're using dynamic memory, you need to make sure you adhere to the rule of 3 or 5 depending on whether or not you're using C++11.
template<typename T>
class llist {
private:
node<T>* head;
public:
llist();
llist(const llist& src);
llist(llist&& src);
llist(~llist);
llist& operator=(const llist& src);
llist& operator=(llist&& src);
void push();
void insert();
};
default constructor
Nothing fancy here.
template<typename T>
llist<T>::llist() : head(nullptr) { }
copy constructor
Since you're using dynamic memory this is crucial
template<typename T>
llist<T>::llist(const llist& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
}
move constructor
template<typename T>
llist<T>::llist(llist&& src) {
// delete current nodes
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
// steal the sources list
this->head = src.head;
src.head = nullptr;
}
destructor
template<typename T>
llist<T>::~llist() {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
}
copy assignment operator
template<typename T>
llist& llist<T>::operator=(const llist<T>& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
return(*this);
}
move assignment operator
template<typename T>
llist& llist<T>::operator=(llist<T>&& src) {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
this->head = src.head;
src.head = nullptr;
return(*this);
}
push member
this is essentially opposite of your add member.
template<typename T>
void llist<T>push(T data) {
node<T>* new_node = new node<T>(data);
if(this->head) {
node<T>* tmp = this->head;
while(tmp->get_next()) {
tmp = tmp->get_next();
}
tmp->set_next(new_node);
} else {
this->head = new_node;
}
}
insert member
This is essentially your add member.
template<typename T>
void llist<T>insert(T data) {
node<T>* new_node = new node<T>(data, this->head);
this->head = new_node;
}
I don't know if this will help, and you probably already have and know most of this, but I hope it helps.
In this code, it would appear that you attempted to 'malloc' space for a "llist" user defined object.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
First, you tagged this as C++. In C++, you simply must use new and delete. The C++ compiler does not associate "malloc" with the ctor / dtor of your user created object called "llist". And I assure you that you really do want to create these two methods, even when each are simple. Really.
On the other hand, the C++ compiler does provide New and Delete, and will automatically invoke the ctor and dtor when appropriate for both dynamic variables (in heap), and automatic variables (on stack). The compiler will not support this with malloc.
Second, your function init() does not return or otherwise deliver the value of the automatic variable you named "list" to any other scope. (Typically, a list lifetime exceeds the life of any function that uses or creates it.)
So your object "list" only exists within the scope of the function init(), within the lifetime of init(). Not very useful.
So the handle to the list of 'malloc'ed things is lost, no longer accessible to anything else. After init(), where did you plan for the listHead to reside?
Even if you used new (and delete) the code still does not deliver the listHead anywhere.
To further your program, you need perhaps 1 of 2 things:
1) a return (from the function) of the "list" handle (I've been calling it "listHead" as you intended, right?)
llist* init() {
llist *listHead = ...
return(listHead);
}
OR
2) a parameter reference which your init function changes. This places the list head outside of init().
void init( llist** listHead) {
llist *list = ...
*listHead = list;
}
You might look into, and take hints from std::list, which has 40+ methods, though you might only need 10. For the methods you plan to implement, you should strive to conform to and use similar names and parameters.
Perhaps you meant to use a class data attribute with the label list (it is quite difficult to imagine this from what you provided). In this case, you should distinguish data attributes names to help you remember what it is, and that it has a different scope. For instance, I would use m_listHead. The prefix m_ (or often, simply the one char prefix '_') simply indicates to the reader that this symbol is a data attribute of a class. This idea is a common c++ idiom, and not enforced by the compiler, but is often part of a coding-standard.
Good luck.
First, I realize that there are multiple topics about this on StackOverflow. I'm having a slight bit of trouble understanding the responses in some of them. I'm creating this in hopes that someone can help me understand the process.
I apologize if this question seems relatively novice, but I am doing my best to understand it.
I'm learning about data structures and have been asked to create a LinkedList implementation file based on a header that was provided.
This is homework, so please no 'here is the exact code' type answers. Pseudocode, steps, and hints are welcome.
Here is the part of the header that I'm working on at the moment:
typedef std::string ElementType;
class LinkedList
{
private:
class Node
{
public:
ElementType data;
Node * next;
Node( )
: data( ElementType( ) ), next( NULL )
{ }
Node( ElementType initData )
: data( initData ), next( NULL )
{ }
}; // end of Node class
typedef Node * NodePointer;
public:
LinkedList( );
/* Construct a List object
Precondition: none.
Postcondition: An empty List object has been constructed.
*/
LinkedList( const LinkedList &source );
/* Construct a copy of a List object.
Precondition: None.
Postcondition: A copy of source has been constructed.
*/
~LinkedList( );
/* Destroys a List object.
Precondition: None.
Postcondition: Any memory allocated to the List object has been freed.
*/
const LinkedList & operator=( const LinkedList &rightSide );
/* Assign a copy of a List object to the current object.
private:
NodePointer first;
int mySize;
};
So far, I've created the destructor, can you check and make sure it is correct?
//Destructor
LinkedList::~LinkedList()
{
NodePointer ptr = first;
while(ptr != 0 ) {
NodePointer next = ptr->next;
delete ptr;
ptr = next;
}
first = 0;
}
Now here is the part where I'm lost...What are the basic steps of creating the copy constructor? I've finished the default constructor which was simple, but I'm a bit confused with what I should be doing on the copy constructor.
I'm also slightly confused about overloading the = operator, I assume it will be very similar to the copy constructor.
Edit
My first attempt at the copy constructor:
LinkedList::LinkedList(const LinkedList & source)
{
//create a ptr to our copy
Node * copy_node = source.first;
//where we will be inserting our copy
Node * insert_node = first;
while(copy_node != nullptr)
{
//insert our new copy node
insert_node = new Node(copy_node->data);
//move to our next node
copy_node = copy_node->next;
if(copy_node != nullptr) {
insert_node = insert_node->next;
} else {
insert_node = first;
}
//update size
mySize++;
}
}
I feel like something is missing there.
What are the basic steps of creating the copy constructor? I've finished the default constructor which was simple, but I'm a bit confused with what I should be doing on the copy constructor.
Well, you need to copy the source, obviously. If the source is a list of N nodes then you need to construct another list of N nodes, with each node being a copy of the corresponding one in the source.
So loop over the source nodes and create copies of them.
I'm also slightly confused about overloading the = operator, I assume it will be very similar to the copy constructor.
Yes, except you need to dispose of the current nodes first. However, a simple and safe way to implement assignment is copy-and-swap, so define a correct swap member:
void swap(LinkedList& other)
{
std::swap(first, other.first);
std::swap(size, other.size);
}
Then use it to implement assignment:
LinkedList& operator=(const LinkedList& source)
{
LinkedList(source).swap(*this);
return *this;
}
This creates a temporary that is a copy of source, then swaps it with *this, so the old contents of *this get destroyed by the temporary, and *this ends up with the copied data.
N.B. the return type should be non-const, it is not idiomatic to return a const-reference from an assignment operator.
The most easiest way is to implement a function which adds new nodes into the list and call it in the loop inside the constructor:
LinkedList(const LinkedList& rhs)
{
Node* node = rhs.first;
while (node) {
add(node.data);
node = node->next;
}
}
void add(ElementType data)
{
Node* node = new Node(data);
// add node somehow
// I'd recommend to keep pointer to the tail
}
Please note this implementation is not exception safe!
EDIT: added copy function for the copy constructor, the first step to the exception safety:
LinkedList(const LinkedList& rhs)
{
copy(rhs.first, first);
}
void copy(Node* src, Node*& dest)
{
// handle the head element separately
if (!src) {
return; // empty list
} else {
dest = new Node(src->data); // equivalent to first = new Node...
src = src->next;
}
// copy the rest of the list
Node* p = dest;
while (src) {
p->next = new Node(src->data);
src = src->next;
p = p->next;
}
}