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.
Related
I am new to c++ and am working on an assignment involving vectors and a doubly linked list. I am given this struct as such.
struct Node {
std::vector<T> data;
Node<T>* next;
Node<T>* prev;
Node(): next(nullptr), prev(nullptr){} };
I am now required in another class to create a constructor for this Node to be used for various methods. I understand that the struct already has an initialization list for next and prev, I think I am just overthinking what the constructor should be.
Class LinkedVector {
Node<T>* head;
LinkedVector<T>::LinkedVector(){
head = NULL;
}
}
Is this the correct way of constructing the linked list? Again I am new to c++ and any help pointing me in the right direction is most helpful. Thank you and have a great day.
Yes, that is almost correct, you want to set head to nullptr instead of NULL of course.
Then when you create methods to add to the list you will need to check if it is empty and if so initialize head rather than adding an element elsewhere.
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)
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;
};
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.
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.