Given the following code:
template<class S, class T>
class node {
public:
S key;
T value;
node<S, T> *right_node, *left_node, *parent_node, *nearest_right_node, *nearest_left_node;
int height;
public:
template<typename, typename> friend
class avl_tree;
node() = default;
node(const S *key, const T *value, node<S, T> *right_node = nullptr, node<S, T> *left_node = nullptr,
node<S, T> *parent_node = nullptr, node<S, T> *nearest_right_node = nullptr,
node<S, T> *nearest_left_node = nullptr) : key(*key),
value(*value),
right_node(right_node),
left_node(left_node),
parent_node(parent_node),
nearest_right_node(nearest_right_node),
nearest_left_node(nearest_left_node),
height(0) {
}
~node() = default;
};
Can I replace the pointers in the c'tor for value and key with references and still able to give key and value a value of nullptr? (for example if S is int*)?
Update:
int* x;
void update_x(int &new_x)
{
x=new_x;
}
can I call update_x(null_ptr)
Your node class has members S key and T value, and constructor parameters const S *key and const T *value which initialize the members using key(*key) and value(*value), respectively, which will copy the input values into the members.
If a nullptr is passed in to either parameter, the code has undefined behavior when dereferencing the nullptr.
There is no reason for the parameters to be pointers at all. They should be passed in either by value:
template<class S, class T>
class node {
public:
S key;
T value;
...
public:
...
node(S key, T value, ...) : key(key), value(value), ... { }
// or: node(S key, T value, ...) : key(std::move(key)), value(std::move(value)), ... { }
...
};
Or by const reference:
template<class S, class T>
class node {
public:
S key;
T value;
...
public:
...
node(const S& key, const T& value, ...) : key(key), value(value), ... { }
...
};
Or by rvalue reference:
template<class S, class T>
class node {
public:
S key;
T value;
...
public:
...
node(S&& key, T&& value, ...) : key(std::move(key)), value(std::move(value)), ... { }
...
};
If S or T are defined as pointer types, then a nullptr can be passed in as the value to be assigned to the key/value members, eg:
node<int, type*> *n = new node<int, type*>(1, nullptr);
type t;
node<int, type*> *n = new node<int, type*>(1, &t);
But if S and T are not pointer types, they should not be passed in as pointers:
type t;
node<int, type> *n = new node<int, type>(1, t);
type t;
node<int, type> *n = new node<int, type>(1, std::move(t));
node<int, type> *n = new node<int, type>(1, type{});
The issue in your code boils down to:
void foo(int* x) {
int y = *x;
}
Passing a nullptr to foo will cause undefined behavior because you shall not dereference a nullptr.
Using a reference is possible but will not change that fact:
void bar(int*& x) {
int y = *x;
}
This is passing the pointer by reference.
int a;
int* p = &a;
bar(p);
Now x in bar is a reference to the pointer to a. However, you cannot pass a nullpointer to bar, because bar is dereferencing the pointer. Some malicious code could do this:
int* p = nullptr;
bar(p); // DO NOT DO THIS !!!
And then int y = *x; will invoke undefined behavior.
If the function expect to always get a valid object it should take it by reference:
void foo_bar(int& x) {
int y = x; // no danger, all fine
}
References cannot refer to nothing. They must be initialzed:
int& x; // ERROR !
int y;
int& z = y; // OK, z refers to y
Pointers on the other hand not always point to a valid pointee:
int* p; // not initialized, no (big) problem
p = nullptr; // still not pointing to an int
int q;
p = &q; // now p points to an int
References cannot refer to nothing, thats why when "nothing" is not a valid parameter you should prefer references over pointers. In your case you always require *value to be valid, hence value always must point to a S and you should use a reference instead to avoid the above mentioned problem.
Concerning your update:
int* x;
void update_x(int &new_x)
{
x=new_x;
}
can I call update_x(null_ptr)
No. You cannot. nullptr is not an int. A reference to an int always references an int.
I've implemented a double linked list using weak and smart pointers. The program is working but I have doubts about that const in the getPrev signature method. If I put const a the end of the method signature it will cause a binding reference error
error: binding reference of type 'std::weak_ptr<Node<Integer> >&' to 'const std::weak_ptr<Node<Integer> >' discards qualifiers
return prev;
Wasn't the purpose of that const to mark *this as const ? The return type is non-const for my understanding.
Here is the code, main.cpp:
#include <memory>
#include <iostream>
#include "DoubleLinkedList.h"
class Integer {
private:
int number;
public:
Integer(int number) : number(number) {}
int get() { return number; }
};
int main() {
DoubleLinkedList<Integer> list;
list.insert(Integer(1));
list.insert(Integer(2));
list.insert(Integer(3));
list.insert(Integer(4));
list.insert(Integer(5));
return 0;
}
DoubleLinkedList.h
#include <memory>
#include <vector>
#include <iostream>
template <typename T>
class Node {
private:
T data;
std::weak_ptr<Node> prev;
std::shared_ptr<Node> next;
public:
Node(): data(0) {}
Node(const T &object) : data(object) {};
T getData() const {
return data;
}
void setData(T data) {
Node::data = data;
}
std::weak_ptr<Node> &getPrev() const {
return prev;
}
void setPrev(const std::weak_ptr<Node> &prev) {
Node::prev = prev;
}
std::shared_ptr<Node> &getNext() {
return next;
}
void setNext(const std::shared_ptr<Node> &next) {
Node::next = next;
}
};
template <typename T>
class DoubleLinkedList {
private:
std::shared_ptr<Node<T>> header;
std::weak_ptr<Node<T>> trailer;
int size;
public:
DoubleLinkedList() : size(0) {}
void insert(const T &value) {
auto node = std::make_shared<Node<T>>(value);
if (size++ == 0) {
header = node;
} else {
auto last = trailer.lock();
last->getNext() = node;
node->getPrev() = last;
}
trailer = node;
}
};
If you are inside a const method, all the data members are considered const.
That is, inside this function:
std::weak_ptr<Node> &getPrev() const
you can imagine the member variables like this:
const T data;
const std::weak_ptr<Node> prev;
const std::shared_ptr<Node> next;
It should be clear that you cannot return a non-const reference to a const object:
const int x;
int& getX()
{
return x; // error
}
The reference would allow you to modify x even though it is const, so this is forbidden (formally: a non-const reference cannot bind to a const object).
Inside a const member function of Node, prev is a const std::weak_ptr<Node>, so a std::weak_ptr<Node>& cannot bind to it for the exact same reason.
It appears that within insert you do intend to modify node (by changing its prev value), in which case the getPrev function should not be const (because you intend to modify the object). But this kind of access should probably be reserved for the DoubleLinkedList and not some arbitrary outside user. It then becomes a question of interface design: Which parts of your code are implementation details and how those should be hidden from users? Which parts are the interface that users should interact with (with minimal opportunity for breaking things)?
Node is a struct which defined in a class List_set private.
struct List_set::Node
{
element_t str_l;
link_t next_ptr ;
static link_t* find(link_t* current, element_t string_t);
Node();
};
List_set::List_set():link_(nullptr)
{}
List_set::Node::Node():next_ptr{0}
{}
I want to use a constructor to initialize the Node, however the compiler always report an error:
no matching constructor for initialization of 'list_set::List_set::Node'
Can you please help to figure it out?
The next is the definition of class List_set hope it can provide some reference.
class List_set
{
public:
using element_t = std::string;
List_set();
List_set(std::initializer_list<element_t>);
bool is_empty() const;
size_t size() const;
bool contains(const element_t&) const;
void insert(element_t);
private:
struct Node;
using link_t = std::shared_ptr<Node>;
link_t link_;
};
Im trying to write an efficient list and node classes that have minimal code to iterate over them. However I'm having difficulty fulfilling all my criteria. I want this code to be used safely in a multi-threaded environment and so if possible I want all the functions and arguments for iteration to be const to ensure no state is being written to ie its all read only. So I wrote the below classes and iteration code:
template<typename _Type_>
class Node
{
template<typename _Type_> friend class List;
public:
Node() : m_Next(NULL) {}
Node(const _Type_& value) : m_Value(value), m_Next(nullptr()) {}
const _Type_& Get() const { return m_Value; }
Node&& Next() const { return std::move(*m_Next); }
operator _Type_ () const { return m_Value; }
_Type_& operator->() { return m_Value; }
const _Type_& operator->() const { return m_Value; }
operator bool() const { return m_Next != nullptr(); }
private:
_Type_ m_Value;
Node* m_Next;
};
template<typename _Type_>
class List
{
public:
typedef Node<_Type_> Node;
List() : m_Head(NULL) {}
void AddHead( const _Type_& value )
{
Node* pNode = GE_NEW(Node, value);
pNode->m_Next = &m_Head;
m_Head = *pNode;
}
Node&& Head() const { return std::move(m_Head); }
private:
Node m_Head;
};
and the all important iteration code:
RenderTargetList::Node&& renderTarget = m_Targets.Head();
while( renderTarget )
{
if(renderTarget->Format() == format)
{
return renderTarget.Get();
}
renderTarget = renderTarget.Next();
}
But this doesn't compile as:
Node&& Head() const { return std::move(m_Head); }
returns a non const rvalue reference in a const function function ie it has to be:
const Node&& Head() const { return std::move(m_Head); }
instead, but then this doesn't work as the iteration code fails on the assignment:
renderTarget = renderTarget.Next();
because renderTarget must now be defined as:
const RenderTargetList::Node&& renderTarget = m_Targets.Head();
because head returns a const. Basically I seem to be in a right mess of const, references, lvalues and rvalues. Someone please help!
Thanks
Here's the basic version of a list class. Note that the iterator type and the node type are two distinct types. This way, the node type can own the value, and the iterator type can have pointer semantics.
I'll post this as a community wiki, as it's rather a comment than a direct answer to the question.
template<typename Type>
class List
{
private:
// note that for immutable nodes, we could store `Type const`
// and for an immutable list, `Node const* const`
struct Node
{
Type m_Value;
Node* m_pNext;
};
Node* m_pHead;
public:
class const_iterator
{
private:
Node const* m_pNode;
friend List;
const_iterator(Node* p_pNode) : m_pNode(p_pNode) {}
public:
const_iterator() : m_pNode(nullptr) {}
explicit operator bool() const
{ return m_pNode; }
const_iterator Next() const
{ return {m_pNode->m_pNext}; }
Type const& Get() const
{ return m_pNode->m_Value; }
friend bool operator!=(const_iterator const& lhs,
const_iterator const& rhs)
{
return lhs.m_pNode != rhs.m_pNode;
}
};
List() : m_pHead(nullptr) {}
~List()
{
// delete nodes
}
List(List const&) = delete; // needs to be customized
List& operator=(List const&) = delete; // this one, too
// the only function that modifies the list:
void AddHead( Type const& value )
{
Node* pNode = new Node{value, m_pHead};
m_pHead = pNode;
}
const_iterator Head() const
{ return {m_pHead}; }
};
Usage example:
#include <iostream>
int main()
{
List<int> l;
for(int i = 0; i < 10; ++i) l.AddHead(i);
auto it = l.Head();
while(it)
{
std::cout << it.Get() << ", ";
it = it.Next();
}
}
I think you are confused at to the point of const if your goal is thread safety -- const does not give you thread safe code, only considerate and well understood access patterns will give you thread safe code.
I have lost of code which is thread sfae which does not make use of const, and you will need to as well if you want to be able to keep the "AddHead" method which certainly in its current form is not thread safe.
If your code is threaded you will need to add mutex locks around the code where you are accessing shared data which could be modified or read by another thread -- the critical zones -- from your code structure these critical zones could be very short lived, so not much of a penalty.
I am trying to have a const operation on a class that is truly const - it does not change data that the class points to.
For example:
class Node{
public:
int val;
};
class V{
public:
Node * node; //what is the change that is needed here?
void const_action()const{
node->val=5; //error wanted here
}
void action(){
node->val=5; //error is not wanted here
}
};
You can use a template to enforce the const correctness on a pointer without changing the meaning or the implementation of your class:
template <typename T>
class PreseveConstPointer
{
T *t_;
public:
PreseveConstPointer(T *t = nullptr)
: t_(t)
{
}
PreseveConstPointer<T> * operator=(T *t)
{
t_ = t;
return this;
}
T* operator->()
{
return t_;
}
T const * operator->() const
{
return t_;
}
T * data()
{
return t_;
}
};
class Node{
public:
int val;
};
class V{
public:
PreseveConstPointer<Node> node;
V()
{
node = new Node;
}
~V()
{
if(node.data())
delete node.data();
}
void const_action()const{
node->val=5; // You will get an error here
}
void action(){
node->val=5; // No error here
}
};
const after a function declaration says that the function is not allowed to change any class members (except ones that are marked mutable).
Since your code doesn't change any class member, and only changes the object node points to, both function will compile.
AFAIK there's no way to prevent this. If you mark the node const, neither will compile.
You're confusing Node* const for Node const*.
An [unfortunate?] side effect of using indirection here is that constness of the pointer member has nothing to do with the actual Node on which you're operating.
If you don't need that member to be a pointer, then this is pleasingly easy:
class V
{
public:
Node node;
void const_action() const
{
node.val = 5; // error here
}
void action()
{
node.val = 5; // no error here
}
};
However, given its name, I suspect life is not that simple and you are basically out of luck.