I am studying C++ from Herbert Schildt's book "Teach Yourself C++ 3rd Edition". In one example code, something made me so confused.
#include <iostream>
using namesapce std;
template <class data_t> class list {
data_t data;
list *next;
public:
list(data_t d);
void add(list *node) {node->next = this; next = 0; }
list *getnext() { return next;}
data_t getdata() { return data;}
};
I didn't write all code in example, I only writed class declaration.
In here, I didn't understand one part. First, the "*node" pointer which belongs to "add" function, is using as "node->next" for "*next" pointer. I didn't understand the aim in here, why we don't directly use like {"next=this; next=0;} ? Also, how can we use an empty pointer (*node) to point another empty pointer (*next) ? I am missing maybe some key concepts, thanks for helps.
In the function add():
template<typename data_t>
class list {
// ...
void add(list *node) {node->next = this; next = 0; }
// ...
};
The point of the function is to add this to the list, as the next element after node. Think about it like a train: add() tells the current car to hook itself onto the back of another car, with the function's parameter telling it which car to link to.
Note that this function expects node to not be a null pointer; if it is null, you'll probably get a segmentation fault.
void add(list* node) {
node->next = this; // Tells "node" to register the current node as its "next" node.
// Should first check whether "node" is a valid pointer.
next = 0; // Tells the current node to register a null pointer as its "next" node,
// signifying that it's currently the last node in the list.
// Note that instead of assigning 0, it should instead assign either
// "nullptr" (C++11 or later) or "NULL" (pre-C++11).
}
Therefore, when used like this:
list<int> start(42), second(24);
second.add(&start);
It will do the following:
(&start)->next = &second;
(&second)->next = 0;
This creates a singly linked list, which looks like this:
Start: "start"
--> Next: "second"
--> Next: None.
Or, more concisely:
start->second->NULL
Remember, when inside a non-static member function, any member variable accesses are assumed to operate on this unless otherwise specified.
template<typename data_t>
void list<data_t>::add(list* node) {
node->next = this; // Operates on node, not on this.
next = 0; // Operates on this; becomes "this->next = 0;"
}
If this function intstead was just {next = this; next = 0;}, then it would be:
template<typename data_t>
void list<data_t>::add(list *node) {
this->next = this; // Bad. Creates a circular linked list, where "this" is ALWAYS the
// next node after "this".
this->next = 0; // Renders the above line entirely pointless, by indicating that there
// are no nodes after "this".
}
Related
I am learning list in C++ independently, and i have searched many websites about it. However, almost every approach to create a list is the same.
They usually create a struct as the node of a class. I want to create a class without using struct. So I created a class name ListNode which contains an int data and a pointer.
The main member functions of my class are AddNode and show.
Although, this program compiles successfully, it still does not work as I wish.
Here is the header file:
#ifndef LISTNODE_H_
#define LISTNODE_H_
#pragma once
class ListNode
{
private:
int data;
ListNode * next;
public:
ListNode();
ListNode(int value);
~ListNode();
void AddNode(ListNode* node,ListNode* headNode);
void show(ListNode* headNode);
};
#endif
Here is the implementation:
#include "ListNode.h"
#include<iostream>
ListNode::ListNode()
{
data = 0;
next = NULL;
}
ListNode::ListNode(int value)
{
data = value;
next = NULL;
}
ListNode::~ListNode()
{
}
void ListNode::AddNode(ListNode* node,ListNode* headNode) {
node->next = headNode;
headNode =node;
}
void ListNode::show(ListNode* headNode) {
ListNode * traversNode;
traversNode = headNode;
while (traversNode != NULL) {
std::cout << traversNode->data << std::endl;
traversNode = traversNode->next;
}
}
Main function:
#include"ListNode.h"
#include<iostream>
int main()
{
using std::cout;
using std::endl;
ListNode* head = new ListNode();
for (int i = 0;i < 3;i++) {
ListNode* Node = new ListNode(i);
head->AddNode(Node, head);
}
head->show(head);
return 0;
}
As far as I am concerned, the output should be
2
1
0
However, the output is a single zero. There must be something wrong in the AddNode and show function.
Could you please tell me what is wrong with these two functions?
When you call head->AddNode(node, head) you´re passing the memory directions which the pointers point, when the function arguments receive those directions, they are now pointing to the same directions, but those are another pointers, no the ones you declared in main. You could see it like this:
void ListNode::AddNode(ListNode* node,ListNode* headNode) {
/*when the arguments get their value it could be seen as something like:
node = Node(the one from main)
headNode = head(the one from main)*/
node->next = headNode;
/*Here you are modifying the new inserted node, no problem*/
headNode = node;
/*The problem is here, you´re modifying the memory direction
headNode points to, but the headNode argument of the function, no the one declared in main*/
}
So the pointer head in main() always points to the same first node you also declared in main().
In order to fix this you should change your code this way:
void ListNode::AddNode(ListNode* node,ListNode** headNode) {
/* second paramater now receives a pointer to apointer to a node */
node->next = *headNode;//the same as before but due to pointer syntaxis changes a bit
*headNode = node;//now you change the real head
}
And when you call it:
head->AddNode(Node, &head);//you use '&' before head
Now the real head, no the one in the function, will point to the last node you inserted.
Hello I am trying to use pointers and learning the basics on unique pointers in C++. Below is my code I have commented the line of code in main function. to debug the problem However, I am unable to do so. What am I missing ? Is my move() in the insertNode() incorrect ? The error I get is below the code :
#include<memory>
#include<iostream>
struct node{
int data;
std::unique_ptr<node> next;
};
void print(std::unique_ptr<node>head){
while (head)
std::cout << head->data<<std::endl;
}
std::unique_ptr<node> insertNode(std::unique_ptr<node>head, int value){
node newNode;
newNode.data = value;
//head is empty
if (!head){
return std::make_unique<node>(newNode);
}
else{
//head points to an existing list
newNode.next = move(head->next);
return std::make_unique<node>(newNode);
}
}
auto main() -> int
{
//std::unique_ptr<node>head;
//for (int i = 1; i < 10; i++){
// //head = insertNode(head, i);
//}
}
ERROR
std::unique_ptr>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
Aside from other small problems, the main issue is this line:
return std::make_unique<node>(newNode);
You are trying to construct a unique pointer to a new node, passing newNode to the copy constructor of node. However, the copy constructor of node is deleted, since node contains a non-copyable type (i.e. std::unique_ptr<node>).
You should pass a std::move(newNode) instead, but this is problematic since you create the node on the stack and it will be destroyed at the exit from the function.
Using a std::unique_ptr here is a bad idea in my opinion, since, for example, to print the list (or insert into the list), you need to std::move the head (so you lose it) and so on. I think you're much better off with a std::shared_ptr.
I was having the same problem and indeed using a shared_ptr works.
Using the smart pointer as an argument in the function copies the pointer (not the data it points to), and this causes the unique_ptr to reset and delete the data it was previously pointing at- hence we get that "attempting to reference a deleted function" error. If you use a shared_ptr this will simply increment the reference count and de-increment it once you are out of the scope of that function.
The comments in the answers above suggest that using a shared_ptr is baseless. These answers were written before the C++17 standard and it is my understanding that we should be using the most updated versions of the language, hence the shared_ptr is appropriate here.
I don't know why we have to expose node type to user in any case. Whole thingamajig of C++ is to write more code in order to write less code later, as one of my tutors said.
We would like to encapsulate everything and leave no head or tail (pun intended) of node to user. Very simplistic interface would look like:
struct list
{
private:
struct node {
int data;
std::unique_ptr<node> next;
node(int data) : data{data}, next{nullptr} {}
};
std::unique_ptr<node> head;
public:
list() : head{nullptr} {};
void push(int data);
int pop();
~list(); // do we need this?
};
The implementation does something what Ben Voigt mentioned:
void list::push(int data)
{
auto temp{std::make_unique<node>(data)};
if(head)
{
temp->next = std::move(head);
head = std::move(temp);
} else
{
head = std::move(temp);
}
}
int list::pop()
{
if(head == nullptr) {
return 0; /* Return some default. */
/* Or do unthinkable things to user. Throw things at him or throw exception. */
}
auto temp = std::move(head);
head = std::move(temp->next);
return temp->data;
}
We actually need a destructor which would NOT be recursive if list will be really large. Our stack may explode because node's destructor would call unique_ptr's destructor then would call managed node's destructor, which would call unique_ptr's destructor... ad nauseatum.
void list::clear() { while(head) head = std::move(head->next); }
list::~list() { clear(); }
After that default destructor would ping unique_ptr destructor only once for head, no recursive iterations.
If we want to iterate through list without popping node, we'd use get() within some method designed to address that task.
Node *head = list.head.get();
/* ... */
head = head->next.get();
get() return raw pointer without breaking management.
How about this example, in addition to the sample code, he also mentioned some principles:
when you need to "assign" -- use std::move and when you need to just traverse, use get()
I would like to understand the following pop-function.
struct list_node{
int key;
list_node* next;
list_node(int k, list_node* n)
: key(k), next(n);
{}
};
class stapel{
private: list_node* top_node;
public: void pop (int value);
};
void stapel::pop()
{
list_node* p=top_node;
top_node=top_node -> next;
delete p;
}
I know the pop-function removes the topmost node of a stack. So you have a pointer p, that points to the same node like the pointer top_node. I have difficulties understanding the next line. top_node->next means the same like (*top_node).next and top_node is of type list_node, which is like a box that consist a key, a next-pointer and their values. Now I can't understand what top_node->next really means. I know next will become the next top_node pointer but why?
void stapel::pop()
{
list_node* p = top_node; // Get pointer to top of stack
top_node = top_node->next; // Find the next item in the stack, assign it to now be the top
delete p; // Delete the current top
}
You basically told the stack that the new "top" is the node that used to be 2nd from the top. You then delete the top node.
The way that the next line is working, is the same way that a "linked list" works. When you construct the stack (or linked list), for each node, you assign the value of that node (key), and a pointer to the next node (next).
top_node = top_node->next;
is simply getting the next member variable of the current node. That member variable happens to be a pointer to the next node, which was assigned when the current node was inserted into the stack.
Edit
As #Edward noted, it would also be a good idea to check that top_node is not nullptr. If it were, you would hit an exception when you tried to access the next member. So you could modify this function as:
void stapel::pop()
{
if (top_node == nullptr)
return;
list_node* p = top_node;
top_node = top_node->next;
delete p;
}
I am working on some binary tree algorithms and need a "find node with searchindex..." function. The design for treenodes is basically
class TreeNode {
int index; // some identifier
TreeNode *left;
TreeNode *right;
}
and a tree is defined by a pointer to the root-node.
My implementation for the search function is:
void Tree::searchNode(TreeNode * root, int nodeIndex, TreeNode *resultNode){
/* Recursive search */
if (root->index == nodeIndex) {
resultNode = root;
} else {
/* search children if the current node is not a leaf */
if(!root->isLeaf()) {
this->searchNode(root->left,nodeIndex,resultNode);
this->searchNode(root->right,nodeIndex,resultNode);
}
}
}
Arguments: *root is the root-node of the tree, nodeIndex is the search-index and *resultNode is the pointer to the found (or not) node in the tree.
The function does not return a reference or pointer to the found node but modifies the pointer resultNode so it points to the found node. The idea is to initialize resultNode with NULL, perform the search and modify it if a match occurs. Otherwise it remains NULL and I can easily check if there are search results or not.
Another class with a tree buildingTree as member utilizes the search-function in this way:
TreeNode *resultNodePtr = NULL;
this->buildingTree->searchNode(this->buildingTree->rootPtr,
currentNodeIndex, resultNodePtr);
// do sth. with resultNodePtr if != NULL
I create *resultNodePtr on the stack because I just need it temporarily inside the function. Is this done correctly? However: The function does not work. resultNodePtr is always NULL, even if the tree contains a node with the search-index. I debugged it very carefully step by step, it detects
(root->index == nodeIndex)
correctly but
resultNode = root;
does not work (I want resultNode to point to the same adress root points to).
Debugger says resultNode before assignment is 0x0, root node is some adress, after the assignment resultNode remains 0x0.
Do I have to overload the operator= in this case for the class TreeNode?
I have tried it:
TreeNode & TreeNode::operator=(const TreeNode & oldTreeNode){
*this = oldTreeNode;
return *this;
// ignore childs for now
}
I am not an expert but this operator= seems trivial. Does it affect the assignment of two TreeNode pointers *node1 = *node2 at all?
Maybe you can help me. Thanks for reading, appreciate your help.
If I find a solution myself I will post it here.
Regards,
Mark
Because you pass resultNode into the function as a pointer by value, its original value never changes. Think of TreeNode* as literally nothing more than a number representing a memory address; when you reassign it:
resultNode = root;
This modifies the copy that searchNode has, but not the original pointer in the code which invokes searchNode. Take this simpler example:
void Foo(int x)
{
x = 100;
}
void Bar()
{
int x = 0;
Foo(x);
// at this point, x is still 0
}
resultNode's value doesn't change from NULL for the same reason that x doesn't change from 0 when the function Bar is invoked. To fix this issue, pass the pointer in as a pointer to a pointer, or a pointer by reference:
void Tree::searchNode(TreeNode* root, int nodeIndex, TreeNode*& resultNode)
{
// same code
}
... or:
void Tree::searchNode(TreeNode* root, int nodeIndex, TreeNode** resultNodePtr)
{
// assign to *resultNodePtr instead
}
Your resultNode pointer is being passed by value, not by reference. So when the function call completes the pointer on the calling side does not receive a value.
Your algorithm looks fine :)
This is a noobie question, but I'm not sure how to pass by reference in C++. I have the following class which sets up a Node and a few functions.
class Node
{
public:
Node *next;
int data;
Node(int dat)
{
next = NULL;
data = dat;
}
Node* getNext()
{ return next; }
void setNext(Node *n)
{ next = n;}
void reverse(Node *root)
{
Node *previous = NULL;
while(root != NULL)
{
Node *next = root->getNext();
root->setNext(previous);
previous = root;
root = next;
}
root = previous;
}
};
Now, the purpose of my little class is to create a singularly linked list and have the ability to reverse it. And it seems to work fine, if I return the node named 'previous' at the end of reverse.
But look at my main function:
int main()
{
Node *root = new Node(1);
Node *num2 = new Node(2);
Node *num3 = new Node(3);
Node *num4 = new Node(4);
root->setNext(num2);
num2->setNext(num3);
num3->setNext(num4);
root->printList();
root->reverse(root);
root->printList();
return 0;
}
printList() was omitted for sake of space, but it just prints a list given a node. The problem is, when root->reverse(root) is called, root doesn't actually end up pointing to 'previous'.
The output would be this:
1
2
3
4
// the value of previous from the reverse function is 4
1
I really don't understand the output. Anyone care to explain what's happening? (Why isn't the list reversed even though if I did something like this root = root->reverse(root) where reverse returns previous, it would) why is it that root now only points to itself? I'm new to c++ and appreciate your help!
C++ has support for reference semantics. Therefore, for a given function:
void foo(Bar& bar);
To pass by reference you do:
int main() {
Bar whatsit;
foo(whatsit);
return 0;
}
That's it!
This is commonly confused with passing a pointer, where for a function such as:
void foo(Bar* bar);
You would do:
int main() {
Bar whatisit;
foo(&whatsit);
return 0;
}
The difference is mostly a matter of semantics:
- A reference is always valid. There is no reason to check for a NULL pointer.
- A pointer could be NULL, and as such, should be checked.
It is, however, possible for a reference to refer to a NULL pointer, however, if the programmer decides to be evil and abuse reference semantics, but the principle remains.
You aren't passing by reference. You are passing a copy of the pointer. This copy still points to the same node, but it is still just a copy with local scope. Basically it is another pointer pointing to the node that the pointer in main is pointing to (ha!). At the end of your function, your assignment is assigning previous to this pointer copy, and then the function ends and the copy goes out of scope. Your pointer in main remains unchanged.
The reason returning/assigning the pointer worked is that this copy which has been set to what you want is returned and assigned to your pointer in main.
You can fix this in a multitude of ways. Pass a reference to your pointer (ugly imo), use references, or return root and do an assignment.
To pass a pointer by reference you can declare reverse as:
void reverse(Node*& root) {
// ...
}