I've created a Node struct to be used in an implementation of binary search tree. It uses shared pointers to keep track of its children:
template <class T> struct Node;
template <class T>
using Node_ptr = std::shared_ptr<Node<T>>;
template <class T>
struct Node {
Node_ptr<T> left;
Node_ptr<T> right;
const T label;
...
};
Now, I'd like to have a function which given some subtree and a value will return either the node of that specific value or the place where such node should be located in future - find_node.
This is how it looks at the moment:
template <class T>
auto* find_node(Node_ptr<T>* node, const T& value) {
for (; *node && (*node)->label != value
; node = value < (*node)->label ? &(*node)->left : &(*node)->right);
return node;
}
Pretty bad. But it works:
template <class T>
class Binary_search_tree {
public:
// correctly inserts consecutive values
void insert(const T& value) {
if (auto* node = find_node(&root, value); !*node)
*node = std::make_shared<Node<T>>(value);
}
...
private:
Node_ptr<T> root;
...
};
I could rewrite find_node to use std::shared_ptr<Node_ptr<T>> instead of Node_ptr<T>* but it would look even worse. Or would it?
How should I handle such situations?
edit: As it's been pointed out, the function can be simplified a bit by taking a reference to starting node, and returning a reference to a node:
template <class T>
Node_ptr<T>& find_node(Node_ptr<T>& node_ref, const T& value) {
auto* node = &node_ref;
...
return *node;
}
Use of a raw pointer is suggested when you have to allow for passing a null pointer - not true in your example; or when you have to pass a non-integral value (true in your case). In the latter case one should still consider passing a reference rather than a raw pointer. This is a generic suggestion - so exceptions may exist.
Having noted that, you could still use a raw pointer in your function here rather safely by making find_node(...) a private function while keeping the insert(...) public. That is safe, since there is no chance of leaving the pointer dangling from inside insert(...).
Essentially we need to guard against two possibilities with raw pointers: #1. prematurely deleting the memory the pointer points to, #2. never deleting the memory that the pointer points to. Neither of this is possible inside your insert(...) function. So you're safe.
On a related note, you might consider having unique_pointer for your nodes when they are created and then converting them into shared pointers if they are to be shared by more child than one: std::move(...).
Related
I have a couple of problems in my code. The first is in the variable "Element" and it works well for me as long as the constructor of the class that sent the template its variables have default values, is there a way to skip the constructor without putting defaults values in the class? And the other problem is when it comes to freeing memory, when T is of the pointer type I will need to do the delete but just as I put the code I get an error, is there any other solution that can help me? I will be attentive to your answers, thanks: D
namespace Linked{
template <class T>
struct Nodo{
const bool isponter = is_pointer<T>::value;
T Element;
Nodo<T> *Next;
Nodo(){
this->Next = nullptr;
}
~Nodo(){
if(is_pointer<T>::value)
delete Element;
}
};
}
namespace Linked{
template <class T>
struct Nodo{
T Element;
Nodo<T> *Next = nullptr;
~Nodo(){
if constexpr (std::is_pointer<T>::value)
delete Element;
}
};
You should also consider if T is pointer to array.
The only syntactial problems with your code are, that you didn't #include <type_traits> and forgot the std:: before is_pointer<T>::value.
But what you are attempting to do will raise problems with ownership. When Nodo contains a pointer it shouldn't own the object that pointer is pointing to. So you can't just delete that pointer, since you can't even know where it is pointing. Consider the following three cases, each of them requires different handling, but you have no way of determining what case you are facing:
Nodo<int*> n1, n2, n3;
n1.Element = new int(1); // requires delete
n2.Element = new int[10]; // requires delete[], crashes with delete
int i = 0;
n3.Element = &i; // no delete at all, crashes with delete
Usually whoever allocated a object on the heap is responsible for deallocating it. Nodo should not attempt to deallocate memory which it hasn't allocated.
As you didn't specify a c++ version, I'm gonna assume you use the latest, which now is C++17. The closest fit to your existing code is using if constexpr, I won't elaborate on that as there are other good answers for that. If you are stuck on C++14 or C++11 (or worse 03/98, in which case you should simply upgrade), you will need to specialize your template. (I'll come back to this)
This code however, is violating one of the CppCoreGuidelines: ES.24: Use a unique_ptr<T> to hold pointers By writing your template to detect raw pointers and delete it, one always has to allocate. Hence your linked list can't refer to some sub-data of something existing. As already eluded to in the comments, if the users want the memory to be cleaned, use std::unique_ptr. An example:
namespace Linked{
template <class T>
struct Nodo{
T Element;
Nodo<T> *Next{nullptr};
Nodo() = default;
~Nodo() = default;
};
}
// Has ownership
auto node = Nodo<std::unique_ptr<int>>{};
node.element = std::make_unique<int>(42);
// Has ownership (to array of 42 elements)
auto node = Nodo<std::unique_ptr<int[]>>{};
node.element = std::make_unique<int[]>(42);
// No ownership
int value = 42;
auto node = Nodo<int>{};
node.element = &value;
With this, ownership is clear to the caller and transparant for you. (as you don't need to know about arrays, std::unique_ptr knows about that) You might want to put some restrictions on T, like adding static_assert(std::is_nothrow_move_constructable<T>);.
This above solution solves the problem in C++11 and upwards and should be the recommended approach.
If not, use if constexpr if your condition isn't capturable in a dedicated class in C++17. And partial specialization in C++14 and C++11.
namespace Linked{
template <class T>
struct Nodo{
T Element;
Nodo<T> *Next{nullptr};
Nodo() = default;
~Nodo() = default;
};
template <class T>
struct Nodo<T*>{
T *Element{nullptr};
Nodo<T> *Next{nullptr};
Nodo() = default;
~Nodo() { delete Element; }
};
}
If you don't want to repeat your code too much
namespace Linked{
template <class T, class Me>
struct AbstractNodo{
T Element;
Me *Next{nullptr};
// All common code
};
template <class T>
struct Nodo : AbstractNodo<T, Nodo<T>>{
Nodo() = default;
~Nodo() = default;
};
template <class T>
struct Nodo<T*> : AbstractNodo<T, Nodo<T*>>{
Nodo() = default;
~Nodo() { delete Element; }
};
}
There is also a way to specialize a single method, however, I'm not that familiar with it, see Stack overflow: Template specialization of a single method from a templated class for more details.
I'm trying to give my generic list class a reverse function. For some reason, my algorithm ain't workin' when I test it. I thought it made sense: swap the pointers to the first and last nodes of the list, then go through the list and for each node swap its pointers to the previous and next node.
Go easy on me, guys. I'm trying to get some practice with generic programming. Teach me the ways of a C++ purist.
Here's the swap function:
template <class T> void swap(T* a, T* b) {
T* tempPtr = a;
a = b;
b = tempPtr;
}
Here's the reverse function:
template <class T> void List<T>::reverse() {
if (size > 1) {
swap(firstNodePtr, lastNodePtr);
node* curNodePtr = firstNodePtr;
while (curNodePtr != NULL) {
swap(curNodePtr->prevNodePtr, curNodePtr->nextNodePtr);
curNodePtr = curNodePtr->nextNodePtr;
}
}
}
Here's the class, its members and prototypes for functions:
template <class T> class List {
public:
List();
~List();
void push_back(T);
void push_front(T);
T get_at(unsigned);
unsigned get_size();
void reverse();
private:
struct node {
T val;
node* prevNodePtr;
node* nextNodePtr;
};
node* firstNodePtr;
node* lastNodePtr;
unsigned size;
};
Your swap<T> function does not work: it exchanges pointers, which are copied by value into local variables of your function, which has no effect in the caller.
Dropping your own swap and replacing it with std::swap will fix this problem.
Since you pass the two pointers by value, the changes to a and b don't propagate out of the swap() function, making it a no-op.
One way to fix it is by passing the pointers by reference:
template <class T> void swap(T*& a, T*& b) {
Alternatively (and preferably) just use std::swap() instead of your own function.
If you exposed your node structure (or at least a bidirectional iterator type for your list), you could avoid the whole issue and just use std::reverse.
List<int> someList;
// fill with data
std::reverse(someList.begin(), someList.end()); // where begin returns a bidirectional iterator for the head, and end returns a bidirectional iterator for 1 element beyond the tail
I have a class, which I'll refer to as myclass, that has a list container of the type T. I also have a couple of methods that remove items from the list. Should the T be a pointer of some sort, I would like to check that it indeed is a pointer and then delete it in order to relieve allocated resources back to memory. Here's a snippet of code:
template<typename T>
class myclass{
private:
std::list<T> * container;
// other vars
public:
void erase(const T &item){
if (!this->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
// this is where i would like to check if *temp is a pointer,
// so that I can assign it to a pointer var, remove it from the list,
// then delete the pointer,
//otherwise just simply remove it from the list.
}
}
};
EDIT
auto temp = container->begin();
I want to know how to determine if *temp is a pointer so that I can do the following:
T * var = *temp;
container->remove(temp); // remove or erase, i can't recall at the moment
delete var;
but I only want to do that if *temp is a pointer
1) Determine if Type is a pointer in a template function
2) How would you know if that pointer is pointing to dynamically allocated memory?
I don't think this is a wise idea. You don't know whether the user has provided pointers to data allocated on the stack, or to data that is managed in some other way (eg with smart pointers).
But to answer the question, look at
std::is_pointer<T>::value // in type_traits header
http://en.cppreference.com/w/cpp/types/is_pointer
This is a C++11 feature.
Sorry, but no: std::list<T>::iterator (which is what begin() will return and therefore will be the type of temp) can't ever be a pointer. It must be a type that (at the very least) overloads pre- and post-increment and decrement to do linked list traversal so ++ will do something like pos = pos->next; and -- will to something like pos = pos->prev;.
If you're trying to figure out if *temp (which will be the same type as T) is a pointer, that's a whole different story. You basically have two routes. The one I'd prefer as a general rule would be to provide a specialization of your class for pointers:
template<typename T>
class myclass{
private:
std::list<T> container;
// other vars
public:
void erase(const T &item){
if (!container->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
container.erase(temp);
}
}
};
template<class T>
class myclass <T *> {
private:
std::list<T> container;
// other vars
public:
void erase(const T &item){
if (!container->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
delete *temp;
container.erase(temp);
}
}
};
The biggest problem with this is that you may end up duplicating a fair amount between the base template and the specialization for pointers. There are a couple of ways of avoiding that. One is to use a base class that implements the common behavior, then derive the two specializations from that to provide the specialized behavior. Another would be to use some enable_if or SFINAE to enable different versions of the erase function depending on whether the contained type is something that can be dereferenced or not.
As an aside, you probably shouldn't have std::list<T> *container; -- it should probably be just std::list<T> container; (or, better still in most cases, std::vector<T> container;)
Isn't it annoying container's don't delete normal pointers? Well in C++ raw pointers don't actually own the object. There could be many pointers pointing to the same object. You need a unique pointer - stl provides one in c++11. When a unique_ptr is removed from the list, it will destroy the object it points to, so there is no need to complicate erase.
#include <list>
#include <memory>
#include <type_traits>
using namespace std;
template<typename t, bool b>
struct Selector {
typedef list<T> container;
};
template<typename t>
struct Selector<t, true> {
typedef list<unique_ptr<T> > container;
};
template<typename T>
class myclass{
private:
Selector<T, is_pointer<T>::value>::container* container;
// other vars
public:
void erase(const T &item){
if (!this->find(item)) // find is defined elsewhere
return false;
auto temp = container->begin();
for (int i = 0; i < container->size(); ++i){
// removing the unique_ptr delete's pointer
}
}
};
I am getting this message with everything that has Node* (this declaration has no storage or type specifier). Could somebody help and please send me in the right direction?
template <typename type>
Node* Stack<type>::pop() {
Node* retNode; // the node to be return
if(tos == NULL) {
cerr << "*** Stack empty ***";
exit(1);
}
else {
retNode = tos; // store the location of tos
tos = tos->getLink(); // move to new tos
retNode->setLink(); // unlink the popped node from the stack
size -= 1;
}
return retNode;
}
I am sure it's dealing with Node* but I just can't figure out what.
Below are my declarations for the node class that are being used in my stack class. Let me know if you need my declarations for the stack class as well because I just cant see the problem.
template <typename type>
class Node<type>{
private:
type data;
Node *link;
public:
Node(type p_item, Node *p_link);
type getData() const;
Node* getLink() const;
void setData(type p_data);
void setLink(Node *node);
};
Node is a class template, so you cannot use Node or Node * as data types. You must add template arguments in angle brackets, e.g. Node<int> or Node<char> * etc.
In the specific example you gave, it seems the following would be appropriate:
template <typename type>
Node<type>* Stack<type>::pop() {
Node<type>* retNode;
/* ... */
return retNode;
}
I.e. the same type argument that is used for Stack should (probably) be used for Node as well.
Two further notes:
It seems odd that, while the Node template appears to implement internal data structures of your stack, Node<type> * pointers are returned by the pop function of the stack. It would seem more natural (and better encapsulation, and more intuitive for the users of your stack) to return type objects.
It also seems odd that the pop function calls exit (and thus brings the entire process to a halt) when the stack is empty. Perhaps returning nullptr, or a dummy object, or throwing an exception (or a similar strategy) would be more appropriate.
I've got a linked list where I save data, and a pointer to next node, Node<T>* next, like this:
template <class T>
struct Node
{
T data;
Node<T>* next;
};
The thing is I want to put in this a post-increment operator, so it returns the previous value of my node, but increment the reference. So if I do this
Node<int>* someNode = someList.SomeNode();
Node<int>* tmp = someNode++;
tmp would be the original someNode value, but someNode would be someNode->next.
is it possible to put an operator in the struct? I've tried to, and searched how to do it, but as I don't deal with operators I don't know how to do.
You cannot add member function to basic type like pointer.
What are you trying to define is an iterator. Use wrapper class over your node pointer to succeed:
template <class T>
struct NodeIterator
{
NodeIterator(Node<T>* current) : current(current) {}
NodeIterator& operator ++() { current = current->next; return *this; }
NodeIterator operator ++(int) {
NodeIterator retVal = *this;
++(*this);
return retVal;
}
T* operator-> () const { return ¤t->data; }
T& operator * () const { return current->data; }
Node<T>* current;
};
See std::slist<> implementation for references. Look at template<typename _Tp> struct _List_iterator. Reading STL implementation is better than many books.
Usage:
NodeIterator<T> it = &node;
++it;
T& t = *it;
Node<T>& operator++(int) {…}
is the member you want to implement.
For your code to work, you'd need to be able to define operator++ for your pointer class. That's not allowed, though. You're welcome to define some other named function, though. For example:
template <typename Node>
Node goto_next(Node& node) {
Node result = node;
node = node->next;
return result;
}
Then you can use it like this:
Node<int>* tmp = goto_next(someNode);
Another option is to provide a real iterator class instead of just using a pointer:
Node<int>::iterator someNode = someList.begin();
Node<int>::iterator tmp = someNode++;
Make your iterator keep a Node<T>* member, and make the ++ operator update that internal pointer before it returns a copy of the iterator object.
You really don't want to do that. The idea of using ++ on a pointer is dangerously close to the common iterator pattern. You should just go the full distance and make a real iterator class. Think of std::list<T>::iterator.
Iterators are very lightweight wrappers to give a sensible interface to a node pointer, which provides things like operator ++ to move to the next node, and overloads operator -> to provide simple access to the node data. Converting client code from using a pointer to using an iterator is very straight-forward because the syntax is almost identical.