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.
Related
So I've got this code:
//movable_ptr.hpp
//Michal Cermak
template<typename T> class movable_ptr;
template<typename T> class enable_movable_ptr {
public:
//default constructor
enable_movable_ptr() {};
//move constructor and assignment
enable_movable_ptr(enable_movable_ptr<T>&& p) {
first_ = p.getFirst();
p.retarget_to(this);
};
enable_movable_ptr<T>& operator=(enable_movable_ptr<T>&& p) {
if (this != &p)
{
first_ = p.getFirst();
p.retarget_to(this);
delete &p;
}
return *this;
};
//retargets all pointers in the linked list to a new address
void retarget_to(T* p)
{
if (first_ != nullptr)
{
auto current = first_;
do
{
current->set(p);
current = current->getNext();
} while (current != first_);
}
};
movable_ptr<T>* getFirst() { return first_; };
void setFirst(movable_ptr<T>* p) { first_ = p; };
private:
movable_ptr<T>* first_ = nullptr;
};
template<typename T> class movable_ptr {
public:
//constructors and stuff...
//access to variables
T* get() {return ptr_; };
void set(T* p) { ptr_ = p; };
movable_ptr<T>* getNext() { return next_; };
void setNext(movable_ptr<T>* p) { next_ = p; };
movable_ptr<T>* getPrevious() {return prev_; };
void setPrevious(movable_ptr<T>* p) { prev_ = p; };
private:
T* ptr_ = nullptr;
movable_ptr<T>* next_ = this;
movable_ptr<T>* prev_ = this;
};
My problem is that I need to give T * to retarget_to, but I use retarget_to(this) in the move constructor and assignment in enable_movable_ptr. That passes it enable_movable_ptr<T> * instead of just T *. The thing is, I assume that T inherits from enable_movable_ptr, which will never be used directly, only through the object that inherits from it. For example:
class A : public enable_movable_ptr<A>
{
public:
int val;
A(int val) : val(val) {}
};
And then used like this:
A x(42);
A y = move(x);
In this case, this would be enable_movable_ptr<A> *, but I need something that would give me A * instead. Basically I need a pointer to the lvalue of the = operator, while inside an overload of said operator. Is there any way to do this or am I asking for something impossible?
I haven't understood your question entirely, because it's not clear what you want to achieve with this enable_movable_ptr class. I think your way of writing operator= is ill-formed. You are trying to explicitly call delete on a pointer to r-value (which may firstly be allocated on stack, and moreover probably will be destroyed later anyway via some other mechanism).
I would suggest to consider copy-and-swap approach for operator=, this would allow you to not worry about checking if you are assigning object into itself. The signature would be enable_movable_ptr<T>& operator=(enable_movable_ptr<T> other) (note passing by value).
According to the documentation for multiset for instance see http://www.cplusplus.com/reference/set/multiset/insert/. It should be possible to insert a const value. In my example the multiset is a collection of pointers, but when I try to insert a const pointer I get an error.
template<typename Key>
struct node_key: public node_base {
node_key(Key k): _key(k) {}
virtual ~node_key() {}
const Key& key() const { return _key;}
protected:
Key _key;
void _copy_key(node_key<Key> *n) const { n->_key=_key;}
};
template <typename Compare>
struct ptr_less_key {
ptr_less_key() : _comp() {}
virtual ~ptr_less_key() {}
template <typename Pointer>
bool operator()(const Pointer& a, const Pointer& b) const { return _comp(a->key(), b->key()); }
Compare _comp;
};
int main() {
typedef node_key<int>* keyp;
std::multiset<keyp,ptr_less_key<std::less<int>>> x;
node_key<int> k(5);
const node_key<int> *p=&k;
x.insert(p); //this fails
return 0;
}
What you are currently doing: You are not trying to insert a const pointer, as you think you do, but a non-const pointer to a const element.
Change this
const node_key<int> *p=&k;
to this
node_key<int> *const p=&k;
to make the const keyword apply on the pointer rather than on what it points to.
Given
struct node {
void member();
void const_member() const;
};
consider the four declarations
node* pointer_to_node;
const node* pointer_to_const_node;
node* const const_pointer_to_node;
const node* const const_pointer_to_const_node;
There are two different aspects of constness: that of the object node and that of the pointer. The first two declare mutable pointers to either node or const node. A conversion from node* to const node* is allowed (and implicit), but not the other way around, as this would allow to modify a const node.
The second two declarations declare the respective pointers to be constant, i.e. these pointers cannot be modified (though the node pointed to by const_pointer_to_node can.
pointer_to_node->member(); // okay
pointer_to_node->const_member(); // okay
pointer_to_node = new node; // okay
pointer_to_node = const_pointer_to_node; // okay
pointer_to_node = pointer_to_const_node; // ERROR
pointer_to_const_node->member(); // ERROR
pointer_to_const_node->const_member(); // okay
pointer_to_const_node = new node; // okay
pointer_to_const_node = pointer_to_node; // okay
pointer_to_const_node = const_pointer_to_node; // okay
pointer_to_const_node = const_pointer_to_const_node; // okay
const_pointer_to_node->member(); // okay
const_pointer_to_node->const_member(); // okay
const_pointer_to_node = new node; // ERROR
const_pointer_to_node = const_pointer_to_node; // ERROR
const_pointer_to_node = pointer_to_const_node; // ERROR
const_pointer_to_const_node->member(); // ERROR
const_pointer_to_const_node->const_member(); // okay
const_pointer_to_const_node = new node; // ERROR
const_pointer_to_const_node = const_pointer_to_node; // ERROR
const_pointer_to_const_node = pointer_to_const_node; // ERROR
#include <iostream>
#include<cstddef>
#include<string>
using namespace std;
class spexception
{
protected:
string description;
public:
spexception();
spexception(const string&);
virtual const string& what();
};
template <class T>
class sp
{
private:
T* p;
sp(T*);
public:
sp();
sp(const T&);
sp(int);
T& operator * ();
T* operator -> ();
sp& operator = (const sp&);
sp& operator = (const T&);
sp& operator = (int);
bool operator == (const sp&);
~sp();
};
spexception::spexception() : description("No description.")
{}
spexception::spexception(const string& s) : description(s)
{}
const string& spexception::what()
{
return description;
}
template<class T>
sp<T>::sp()
{
p = NULL;
}
template<class T>
sp<T>::~sp()
{
if(p!=NULL) delete p;
}
template<class T>
sp<T>::sp(const T& t)
{
p = new T(t);
}
template<class T>
sp<T>::sp(int i)
{
if(i!=0) throw spexception("Smart pointer cannot be initialized from a non-zero integer.");
p = NULL;
}
template<class T>
sp<T>& sp<T>::operator = (const sp& q)
{
if(*this==q) return *this;
if(p!=NULL) delete p;
p = q.p;
return *this;
}
template<class T>
sp<T>& sp<T>::operator = (const T& t)
{
p = new T(t);
}
template<class T>
sp<T>& sp<T>::operator = (int i)
{
if(i!=0) throw spexception();
p = NULL;
}
template<class T>
bool sp<T>::operator == (const sp& q)
{
return(p==q.p);
}
template<class T>
T& sp<T>::operator * ()
{
return *p;
}
template<class T>
T* sp<T>::operator -> ()
{
return p;
}
using namespace std;
class node
{
public:
int val;
sp<node> next;
node()
{
val = 5;
}
node(int v) : val(v)
{
cout<<"\nNode with value "<<val<<" created.\n";
}
~node()
{
cout<<"\nNode with value "<<val<<"destroyed.\n";
}
};
class list
{
sp<node> first;
sp<node> last;
public:
list()
{
first = NULL;
last = NULL;
}
void add(int v)
{
if(last==NULL)
{
last = node(v);
first = last;
//last->next = NULL;
}
else
{
last->next = node(v);
//last->next->next = NULL;
last = last->next;
}
}
};
main()
{
list l;
l.add(10);
l.add(20);
l.add(30);
l.add(40);
}
The output is "Node with value 40 destroyed" printed infinite times. According to gdb debugger, the problem happens in the list destructor. According to the gd debugger, there is some node which has a smart pointer pointing to the same node. So when the destructor is called it is supposedly being called infinite times. But I dont see this happening in the code. What exactly is the problem?
EDIT: As molbdnilo pointed out, when the destructor for the 'last'smart pointer is called, an attempt is made to delete a dangling pointer. This should cause a crash. However the program is going into an infinite loop instead. Is this a bug with mingw compiler?
template<class T>
sp<T>& sp<T>::operator = (const sp& q)
{
if(*this==q) return *this;
if(p!=NULL) delete p;
p = q.p;
return *this;
}
In the Above Function The Pointer 'p' gets deleted and next instruction tries to assign a value 'q.p' to the deleted 'p',which can cause problem.
I made changes to above function as given below.
template<class T>
sp<T>& sp<T>::operator = (const sp& q)
{
if(*this==q) return *this;
if(p!=NULL) p = q.p;
return *this;
}
This worked out well.
You have no copy constructor, so the default copy constructor will just copy the pointer, giving you two smart pointer objects that point at the same object.
Similarly, you assignment operator just copies the pointer, giving you two smart pointer objects that point at the same object.
Whenever either of these happens, the destructor for the first smart pointer will delete the object, leaving the other smart pointer dangling. When the second smart pointer is destroyed, it deletes the already deleted pointer again, causing undefined behavior.
When you copy or assign a smart pointer, you need to either also clone the pointed at object, or have a reference count somewhere that tracks how many pointers point at the same object. In this latter case, you only delete the object when the refcount drops to 0.
The code is really simple, consisting of nested similar method calls on different classes, but the program keeps segfaulting on random occasions (always during the Add method though).
I call method Add on class CScreen instance to add an object CCircle, which is cloned (a new instance is created) and pointer is passed down hierarchically until it reaches xnode (my own implementation of list node).
int main (){
CScreen S1;
S1 . Add ( CCircle ( 3, 20, 20, 15 ) );
}
class CScreen {
public:
Qlist <Planimetric*> objects;
void Add (const Planimetric & ob ) {
objects.push_back( ob.clone() ); // returns pointer to new instance
}
...
template<typename _Type>
class Qlist{
public:
Qnode <_Type> *start;
Qlist() : start(null) {
start = new Qnode<_Type>(-coordInfinity, -coordInfinity, coordInfinity, coordInfinity);
}
void push_back (const _Type & data) {
start->push_back(data);
}
...
template<typename _Type>
struct Qnode{
xlist <_Type> objects;
void push_back (const _Type & data) {
objects.push_back(data);
}
...
template<typename _Type>
class xlist{
public:
int sizeOfList;
xnode<_Type> *first, *last;
void push_back (const _Type & data) {
sizeOfList ++;
xnode<_Type> *nnp; // new node pointer ;)
if(first == null)
first = last = new xnode<_Type> (data);
else
last = last->next = nnp = new xnode<_Type>(data, last);
}
...
template<typename _Type>
struct xnode{
_Type data;
xnode *prev, *next;
xnode(const _Type & data, xnode* prev = null, xnode* next = null)
: data(data), prev(prev), next(next) {}
...
class CCircle : public Planimetric {
public:
long long int x,y;
long long int rsq;
CCircle * clone () const {return new CCircle(*this);}
CCircle (int ID, int x, int y, int r) : Planimetric(ID), x(x), y(y) { ...
...
}
I thought it might be a stack overflow because of the high overhead, but it sometimes segfaults on first call. The xlist and xnode implementation worked perfectly until i implemented Qlist.
I'm passing down const pointer reference, which is then copied in constructor in xnode. Could the problem be there? I've tried to debug with gdb, with no luck.
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.