Design
I would note you have not made a linked list, but a chain of Node(s). This may seem to be the same thing but I would see this as different as a linked list would encapsulate and hide its internal structure with a class and provide methods to access the data.
So you have:
void insert(Node*& head, int data);
void print_list(Node* list)
I would have done this:
class LinkedList
{
public:
insert(int data);
print();
};
// Usage in main would then be:
int main()
{
LinkedList my_list;
my_list.insert(10);
my_list.insert(20);
my_list.print();
}
Notice there are no pointers anywhere to be seen for the user. So the user of the class does not need to worry about memory management at all when using the list. Your design on the other hand leaks the list at the end.
Questions:
Should I replace the raw new with a braced initializer?
I would advise it. It avoids a few corner cases where things can go wrong.
Should I replace the Node* with a shared_ptr?
No. Two reasons. One: std::unique_ptr
should be your first smart pointer you consider. Two: this is a container.
There are two types of memory management structures.
- Smart Pointers
- Containers.
Both have to deal with pointers internally (just not expose them to the client). But implementing a container with a smart pointer is potentially inefficient (it can be done). It is usual to do the memory management internally.
Code Review.
Don't expose implementation details
This should be a private member of your linked list.
struct Node {
int data;
Node* next;
};
Allowing users to see the structure allows them to build code around this structure which locks you into maintaining this interface. So you can now no longer improve your linked list.
Also exposing a RAW pointer leads to memory management issues. Is this an owning RAW pointer or not? i.e. who is responsible for calling delete?
Sure you can write it like this:
void insert(Node*& head, int data) {
Node* new_node = new Node;
new_node->data = data;
new_node->next = head;
head = new_node;
}
But that seems overly verbose.
void insert(Node*& head, int data) {
head = new Node{data, head};
}
Your print only prints to std::cout
:
void print_list(Node* list) {
Node* p {list};
std::cout << "Printing list: " << std::endl;
if (p) {
std::cout << p->data;
p = p->next;
while(p) {
std::cout << " -> " << p->data;
p = p->next;
}
std::cout << std::endl;
}
}
It would be nice if it printed to any stream. Then you can print it to a string or a file. Even if the default is std::cout
.
void print_list(Node* list, std::ostream& str = std::cout)
Also, the standard way to print things in C++ is to use operator<<
. So it would be nice if you wrote the appropriate operator.
std::ostream& operator<<(std::ostream& s, Node* node)
{
print_list(node, s);
return s;
}
Const correctness
A big push in C++ is const correctness. This is marking parameters and members as const if they are not mutated/mutating. In the above code the print is not supposed to mutate the list. So it might be nice to mark the nodes as const
.
new
instead ofmalloc
both of which aren't (tightly) coupled to the management of the data structure. \$\endgroup\$