Accessing objects from a linked list (custom implementation) - c++

When using a linked list that stores numerous objects how would you go about access the data inside said object?.
Example code.
using namespace std;
typedef struct node
{
int data;
node* next;
} *nodePtr;
nodePtr head;
nodePtr current;
nodePtr temp;
void PrintList()
{
current = head;
while(current != NULL)
{
cout << current->data.getMakeModel();
cout << current->data.getRegNo();
cout << current->data.getEngineSize();
cout << current->data.getRented();
current=current->next;
}
}
My current way of doing it doesn't work and I'm not sure how to solve it.
All i need to do is access the template object data members which i have getter methods for and output the data.
Any ideas?
On a side note, would it be possible to search for a specific object(an object with specific data member value) in a linked list? with the objects still using templates of course

When using a linked list that stores numerous objects how would you go about access the data inside said object?
If you have a nodePtr you'll have to do ptr->data to access the int of the struct.
My current way of doing it doesn't work and I'm not sure how to solve it.
You are trying to access member functions on an object of type int, that does not have any. You probably meant to define another type for the data member object of node.
On a side note, would it be possible to search for a specific object(an object with specific data member value) in a linked list?
Of course, the implementation of such an algorithm is pretty trivial. You can implement it along the lines of:
nodePtr find(nodePtr head, int value) {
for (; head != NULL; head = head->next)
if (*head == value)
return first;
return NULL;
}
What I would suggest is to use the standard std::forward_list or std::list. If you do, you'll be able to use std::find for the "find"-algorithm.

The probem here is that data is of type int and not of a class type. You will have to change its type to a class type e.g Foo. A more advanced solution will use templates to make it work with arbitrary types (e.g std::list)
Also notice that your class definition is not considered good style. I would define it as:
struct node
{
typedef node* Ptr;
Foo data;
node* next;
};
While the typedef is only reasonable in some special cases (e.g if working extensively with smart pointers and reference counting). In normal cases one should go with node* (which is also smaller o.O)

Related

Class with pointers and const method

Suppose we have class for linked list of T.
(I don't know if code actually works)
template <typename T>
struct List{
struct Node{
T data;
Node *next;
};
// add, remove etc...
Node *locate(const T &a) const{
for(Node *node = head; node; node = node->next)
if (node->data == a)
return node;
return nullptr;
}
private;
Node *head;
};
If you inspect method locate, it basically can harm and ruin the linked list, even the method is marked as const.
I just noticed something else. Since Node is not const, Node::data can be changed as well.
Supposing this was programmer mistake, is there a C++ way this to be avoid?
I know the method can be written as this.
const Node *locateConst(const T &a) const{
for(const Node *node = head; node; node = node->next)
if (node->data == a)
return node;
return nullptr;
}
You are seeking an experimental feature, yet unavailable on many platforms. If your std libray supports, you can use std::experimental::propagate_const<node*> instead of naked raw pointers.
https://en.cppreference.com/w/cpp/experimental/propagate_const
In the mean time, you can implement your - maybe less generic - own version.
In C++, "const" means "can't change this object", it does NOT mean "can't change things this object points to".
I.e. with const List *const_list you can't change const_list->head, but you can change const_list->head->next or const_list->head->data.
If you're writing a List class, then you probably want const List to mean "can't change things this object points to", and C++ provides the tools you need to implement List so it works that way. But it's your job, as the author of the List class, to do that - the compiler can't just magically know that's what you intended.
is there a C++ way this to be avoid?
No, of course not. Programmers do have responsibilities you know. The way the method is declared promises not to change head, not *head.

Why the first node of a linked list is declared as a pointer?

Now I know that why pointers are used in defining linked lists. Simply because structure cannot have a recursive definition and if there would have been no pointers, the compiler won't be able to calculate the size of the node structure.
struct list{
int data;
struct list* next; // this is fine
};
But confusion creeps up when I declare the first node of the linked list as:
struct list* head;
Why this has to be a pointer? Can't it be simply declared as
struct list head;
and the address of this used for further uses? Please clarify my doubt.
There's no definitive answer to this question. You can do it either way. The answer to this question depends on how you want to organize your linked list and how you want to represent an empty list.
You have two choices:
A list without a "dummy" head element. In this case the empty list is represented by null in head pointer
struct list* head = NULL;
So this is the answer to your question: we declare it as a pointer to be able to represent an empty list by setting head pointer to null.
A list with a "dummy" head element. In this case the first element of the list is not used to store actual user data: it simply serves as a starting "dummy" element of the list. It is declared as
struct list head = { 0 };
The above represents an empty list, since head.next is null and head object itself "does not count".
I.e. you can declare it that way, if you so desire. Just keep in mind that head is not really a list element. The actual elements begin after head.
And, as always, keep in mind that when you use non-dynamically-allocated objects, the lifetime of those objects is governed by scoping rules. If you want to override these rules and control the objects' lifetimes manually, then you have no other choice but to allocate them dynamically and, therefore, use pointers.
You can declare a list such a way
struct list head = {};
But there will be some difficulties in the realization of functions that access the list. They have to take into account that the first node is not used as other nodes of the list and data member of the first node data also is not used.
Usually the list is declared the following way
struct List
{
// some other stuff as for example constructors and member functions
struct node
{
int data;
struct node* next; // this is fine
} head;
};
and
List list = {};
Or in C++ you could write simply
struct List
{
// some other stuff as for example constructors and member functions
struct node
{
int data;
struct node* next; // this is fine
} head = nullptr;
};
List list;
Of course you could define the default constructor of the List yourself.
In this case for example to check whether the list is empty it is enough to define the following member function
struct List
{
bool empty() const { return head == nullptr; }
// some other stuff as for example constructors and member functions
struct node
{
int data;
struct node* next; // this is fine
} head;
};
In simple terms, if your head is the start node of the linked list, then it will only contain the address of the first node from where linked list will begin. This is done to avoid confusion for a general programmer. Since the head will contain only address, hence, it is declared as a pointer. But the way you want to declare is also fine, just code accordingly. Tip: If you later on want to make some changes in your linked list, like deletion or insertion operations at the beginning of the linked list, you will face problems as you will require another pointer variable. So its better to declare the first node as pointer.

Some pointers for creating a singly linked list in C++

I have a uni assignment in which I have to implement a singly linked list that contains different objects that are derived from a common abstract base class called Shape.
I'll link to GitHub for the class implementation: shapes.h , shapes.cpp. So far it consists of Shape and its derived class Circle. There'll also be Rectangle, Point and Polygon later.
I should now implement a singly linked list of these different kinds of shapes. So far I've come up with the following class prototype for the List-class and the Node-class:
class Node
{
public:
Node() {}
friend class ShapeList;
private:
Shape* data;
Node* nextNode;
};
class ShapeList
{
public:
ShapeList(){head = NULL;}
void Append(Shape& inData);
private:
Node* head;
};
Adding elements void Append(Shape& inData) to a ShapeList-object should be able to be called from main in the following style:
ShapeList list1;
list1.Append( Circle(5,5,5) );
list1.Append( Rectangle( 4, 10, 2, 4) );
Given this information, how should I go about implementing void Append(Shape& inData)? I've tried several different approaches, but haven't come up with the correct solution so far.
It's also completely possible that the parameter to Append should be something else than (Shape& inData).
edit:
I've implemented Append(Shape& inData) but it works only sometimes:
Circle circle1;
ShapeList list1;
list1.Append( circle1 );
but not with
ShapeList list1;
list1.Append ( Circle(5,5,5) )
So far my Append()-implementation looks as follows:
void ShapeList::Append(Shape& inData)
{
//Create a new node
Node* newNode = new Node();
newNode->data=&inData;
newNode->nextNode=NULL;
//Create a temp pointer
Node *tmp = head;
if (tmp != NULL)
{
//Nodes already present in the list
//Traverse to the end of the list
while(tmp->nextNode != NULL)
tmp = tmp->nextNode;
tmp->nextNode=newNode;
}
else
head=newNode;
}
Does that look ok to you guys?
Since this is tagged under 'homework', I will only point you to the good direction. This may be too basic or maybe it is enough for your needs...
In a typical situation, you would simply use a container that is already written such as std::list.
But for implementing your own linked list
When you start from the head member of the ShapeList, you should be able to traverse the entire list and find a node for which 'nextNode' has never been assigned.
This is where you want to add a new node.
Now thee a a few tricks to be make things work:
1- In C++, variables are not automatically initialized. You must therefore initialize the many values when you create a new node, especially the next node pointer.
2- Instead of having pointers to references, I suggest that either you create copies of Shapes, of use some kind of smart pointers to avoid copying.
3- Don't forget about memory management, when you destroy your linked list, you will have to destroy all nodes individually since.
One very nice implementation of the singly linked list is as a circular list with the "head" pointer pointing at the tail. This makes it easy to insert at either the front or append to the end: in either case you create a fresh node, make the current tail point to it, and make it point to the current head, and then in the insert case make the head pointer point to the new node.
What you appear to be missing (other than what's already been pointed out: allocating, deallocating, and copying the nodes) is a way to know that you've actually created the list. So you'll want to add in some sort of output - either an operator << or a print() routine, which will walk the list, and call your graphical objects' printing mechanisms in order.
You say that it is possible that the argument to Append might not be Shape &data. Given the requirement of the calling convention specified, it should be:
Append( const Shape &data ) // provided shapes have copy constructors
{
Node *newNode = new Node( data ); // requires a constructor of Node that copies data to a freshly allocated location and sticks a pointer to that location in its data field - then Node's destructor needs to release that pointer.
... ( and the code to manipulate the existing list and newNode's next pointer )
}
Among other things this makes responsibility for management clear and simple.
If you have a Node constructor that takes both a pointer to a Node and a Shape, you should be able to do Append in two lines - one allocating the new Node and calling the constructor appropriately, and one modifying a pointer to point to the new node.
I would add - based on your edit - that you absolutely need to do the allocation and copy inside Append.
You probably want Node to be nested inside of ShapeList so its full name will be ShapeList::Node, not just ::Node.
Since Node will own some data remotely, you probably need to define the big three for it.
In line with that, when you push something onto the list, the list will hold a dynamically allocated copy, not the original object.
Edit: Append should take a Shape const & rather than a Shape &. A reference to const can bind to a temporary object, but a reference to non-const cannot, so the calls using parameters that create temporary objects (e.g., list.Append(Circle(5,5,5))) won't compile if the parameter is a reference to non-const object.
I'd also change Node::Node to require that you pass it a parameter or two. As-is, your linked-list code is dealing with the internals of a Node more than I'd like. I'd change it to something like:
Node::Node(Shape const *d, Node *n=NULL) : data(d), nextNode(n) {}
Then in append, instead of:
Node* newNode = new Node();
newNode->data=&inData;
newNode->nextNode=NULL;
You'd use something like:
Node *newNode = new Node(&inData); // or, probably, `... = new Node(inData.clone());`
...and Node's ctor would handle things from there.
Also note that it's easier to add to the beginning of a linked list than to the end (it saves you from walking the whole list). If you really want to add to the end, it's probably worthwhile to save a pointer to the last node you added, so you can go directly to the end, rather than walking the whole list every time.
Here is one way to handle the polymorphic requirement (std::shared_ptr), demonstrated with the STL singly linked list...
typedef forward_list<shared_ptr<Shape>> ShapeList;
ShapeList list1;
list1.push_back(make_shared<Circle>(5,5,5));
list1.push_back(make_shared<Rectangle>(4, 10, 2, 4));
Here is how it would effect Node:
class Node
{
public:
Node() {}
friend class ShapeList;
private:
shared_ptr<Shape> data;
Node* nextNode;
};
and ShapeList...
class ShapeList
{
public:
ShapeList(){head = NULL;}
void Append(const shared_ptr<Shape>& inData);
private:
Node* head;
};

C++: Linked List Representation

The way I know how to represent a linked list is basically creating a Node class (more preferably a struct), and the creating the actual linkedList class. However, yesterday I was searching for the logic of reversing a singly linked list operation and almost 90% of the solutions I've encountered was including that the function, returning data type Node* . Thus I got confused since if you want to reverse a list no matter what operation you done, wouldn't it be in the type of linkedList again? Am I doing it the wrong way?
The linked list implementation I do all the time;
#include <iostream>
using namespace std;
struct Node
{
int data;
Node *next;
};
class linkedList
{
public:
Node* firstPtr;
Node* lastPtr;
linkedList()
{
firstPtr=lastPtr=NULL;
}
void insert(int value)
{
Node* newNode=new Node;
newNode->data=value;
if(firstPtr==NULL)
firstPtr=lastPtr=newNode;
else {
newNode->next=firstPtr;
firstPtr=newNode;
}
}
void print()
{
Node *temp=firstPtr;
while(temp!=NULL)
{
cout<<temp->data<<" ";
temp=temp->next;
}
}
};
You approach isn't wrong, but you might be giving too much emphasis on your linkedList class.
What does that class actually contain? A pointer to the first node, and a pointer to the last node (which is redundant information, since you can find the last node by only knowing the first one). So basically linkedList is just a helper class with no extra information.
The member functions from linkedList could easily be moved inside Node or made free functions that take a Node as parameter.
Well, what is a linked list but a pointer to the first node? A list is fully accessible provided you can get to the first node, and all you need for that is a pointer to the first node.
Unless you want to store extra control information about the list (such as its length for example), there's no need for a separate data type for the list itself.
Now some implementations (such as yours) may also store a pointer to the last node in the list for efficiency, allowing you to append an item in O(1) instead of O(n). But that's an extra feature for the list, not a requirement of lists in general.
Those functions might be returning of type Node* because after reversing the linked-list they will return the pointer to the First node of the list.

C++ Class Error

in Line 3 "Node next;" the Compiler gives me an incomplete type error. I think it treat it as a member function not an Attribute. What's wrong in that Definition?
class Node {
private:
Node next;
Node previous;
int value;
int min;
public:
Node(int value) {
this.value = value;
this.min = value;
}
void insert(Node node) {
if (this.next != null) {
this.next.insert(node);
}
else {
this.next = node;
node.previous = this;
}
}
}
First of all, this is a pointer, not a reference, so you need to replace all this. with this->.
Secondly, you cannot store an instance of a class inside that class because the size of the class would be incalculable. You can only store pointers or references to the class inside the class itself, because the compiler can calculate the size of a pointer or reference to any class without having to have any details of the class (because these pointers/references are all the same size regardless of the underlying object). Change
Node next;
Node previous;
to
Node* next;
Node* previous;
// or Node* next, *prev;
And change
void insert(Node node)
to
void insert(Node* node)
You also need to initialise the member pointers to NULL (not null) in the constructor of your object (because C++ doesn't initialise variables that are intrinsic (built-in) types (like pointers) for you):
Node(int value) {
this->previous = NULL;
this->next = NULL; // or: previous = next = NULL;
this->value = value;
this->min = value; // or: this->value = min = value;
}
// or with initialiser lists (but don't get confused if you don't understand)
// Node(int value) : previous(), next(), value(value), min(value) { }
Additionally, I (like everyone else) have deduced (from your use of objects like they are references and your use null instead of NULL and of the words 'method' and 'attribute') that you are a Java or C# programmer learning C++. Please stop now and read a good book on C++, because C++ is nothing like Java or C#, and if you try to use your C#/Java experience when programming C++ then you will only end up ripping your own head off out of frustration.
Work with pointers, not objects.
Node * next;
Node * previous;
then
Node(int value) : next(NULL), previous(NULL) {/**/}
then
void insert(Node * node) {/**/}
It looks like you're trying to write Java/C# in C++!
In C++, objects are stored by value, not by reference. So in something like this:
class Node {
Node next;
};
you're asking for a Node to store another Node inside it, which would require an infinite amount of storage.
Perhaps you want to store pointers to the next and previous nodes.
You can't nest objects like that. Think about it -- what is the size of Node? Well, since each Node contains exactly 2 other Nodes, it means it would have infinite size.
You need a way to make your children null, so the solution is to use Node* instead of Node.
C++ does not have a concept of "class attributes". Any variable declared at class scope is a (possibly static) member. Therefore, a class that contains a member of its own type is an absurdity. For example,
struct S {
S s;
int i;
};
being legal would mean that sizeof(S) == sizeof(S) + sizeof(int), ignoring padding. As this clearly cannot be the case, it is forbidden.
If you want a Node to have pointers to other Nodes (which you probably do), you should give it Node* members. You should also be passing a Node* to insert, as opposed to a Node*.
By the way, C++ does not generally have null defined like C# does. You can compare pointers to NULL, 0, or nullptr.
The class Node is being defined and while doing so, you cannot define any member of type Node, because the size of Node is not yet determined. Just think, how would compiler computer the size of Node? To compute sizeof(Node), the compiler needs to know the size of each of its members, but two of its members are of type Node itself whose size is being computed.
The solution is to define them as pointer :
Node *next;
Node *previous;
Now the size of Node is computable, as size of each of its members is already known. Note that size of pointer of any type is equal to sizeof(void*)1, which means sizeof(Node*) is equal to sizeof(void*) which is known.
1. Except sizeof(pointer-to-member) could be different from sizeof(void*). Technically, for many compilers, pointer-to-member is not pointer to begin with; they are different animals.