I did huge review of linkedlist implementations and all of them include separate new
call fornode
part for entry, which has *next
and T* entry
values.
As a person with strong C knowledge, I know what memory fragmentation means and I want to avoid too many dynamic allocations using new
keyword. Application will be for embedded systems and as a first part, I have my own new
and delete
operators, overloaded.
I want that my linkedlist node is part of my class, thus I created some test code. I'm not expert in C++ and I want you to take a look into the code and tell me if this code even makes any sense.
Idea is like this:
- There is one template class
LinkedListRoot
which holds information aboutfirst
,last
entries andcount
as number of elements on a list. To add a new entry to list, you have to calladd
method of root entry. - There is second template class
LinkedListNode
which holds information aboutnext
element on a list,prev
element on linked list andpointer to actual entry
. I don't want to dynamically allocate this for every entry, but I want that this is already part of entry when I allocate entry itself. - Then I have a
Widget
class which uses linkedlist approach, and class also hasLinkedListRoot
class which allows me that each widget has children widgets.
I did LinkedList.hpp file:
#pragma once
//Node class
template <typename T>
class LinkedListNode {
public:
LinkedListNode<T>* prev;
LinkedListNode<T>* next;
T* entry;
//Constructors
LinkedListNode() {}
LinkedListNode(T* t) {
entry = t;
}
//Get next node of current node
LinkedListNode<T>* get_next(void) {
return next;
}
//Get node class
T* get(void) {
return entry;
}
};
//Root class, holding first and last entry
template <typename T>
class LinkedListRoot {
public:
LinkedListNode<T>* first;
LinkedListNode<T>* last;
size_t count;
//Default constructor
LinkedListRoot() {
first = nullptr;
last = nullptr;
count = 0;
}
//Add new node to root list, put it to the end
void add(LinkedListNode<T>* node) {
node->next = nullptr;
if (first == nullptr || last == nullptr) {
node->prev = last;
first = node;
last = node;
} else {
node->prev = last;
last->next = node;
last = node;
}
count++;
}
//Get number of entries
size_t getCount() const {
return count;
}
//Get first entry
LinkedListNode<T>* get_first() {
return first;
}
};
After that, I have a widget class which accepts parent widget on constructor and adds widget to parents' linked list (if parent is available):
#pragma once
#include "linkedlist.hpp"
//Class uses base class as I want LinkedListNode is
//included in single new operator call
class Widget: public LinkedListNode<Widget> {
public:
float x, y, width, height;
Widget* parent;
//List of children widgets
LinkedListRoot<Widget> children;
//Empty constructor
Widget() { }
//Add new child widget to current widget
void add_child(Widget* widget) {
//Add to children linkedlistroot
children.add(widget);
}
//For constructor call parent constructor too
//and give pinter to current class
Widget(Widget* parent_widget):LinkedListNode(this) {
parent = parent_widget;
if (parent != nullptr) {
parent->add_child(this);
}
}
};
And I use this code in the main. Please pay attention to print_list
function
#include "stdafx.h"
#include "stdint.h"
#include "stdlib.h"
#include "widget.hpp"
//Root list of widgets
LinkedListRoot<Widget> widgets;
//3 widgets, window1 and window2 should be children of base
Widget *base;
Widget *window1;
Widget *window2;
//Debug widgets
void
print_list(LinkedListRoot<Widget>* top) {
static int deep = 0;
//If no top widget, use default one
if (top == nullptr) {
top = &widgets;
}
//Check if widgets available
if (top->getCount()) {
//It doesn't look perfect this for loop arguments
for (Widget* w = top->get_first()->get(); w != nullptr; w = w->get_next()->get()) {
//Print tree level and width
printf("%d: Width: %d\r\n", (int)deep, (int)w->width);
deep++;
print_list(&w->children);
deep--;
}
}
}
int main() {
//Create base widget without parent
base = new Widget(nullptr);
base->width = 10;
//Since we have no parent, manually add widget to root linkedlist
widgets.add(base);
//Create 2 windows and set base as parent
window1 = new Widget(base);
window1->width = 20;
window2 = new Widget(base);
window2->width = 30;
//Debug widgets
print_list(nullptr);
return 0;
}
And it outputs result which seems ok:
0: Width: 10
1: Width: 20 <-- This one is children of first one
1: Width: 30 <-- This one is on same level as one top, children of first one
Is this a good approach at all? Did I miss a point of C++ doing it this way? Is there better solution?
class Widget: public LinkedListNode<Widget>
. I don't understand what that would mean conceptually. AWidget
is a linked list node of typeWidget
? Isn't that a circular definition? \$\endgroup\$Widget
class and not second one forLinkedListNode
class. \$\endgroup\$