I am working on a binary search tree, where the order matters.
If a node/ nodes get modified, the tree need update to keep the order right.
What I want to do is overload the visiting methods, such as preOrderTraversal; so that it can distinguish whether the function will modify the nodes or not.
void preOrderTraversal(std::function<void(Type)> visit); // read only visit
and
void preOrderTraversal(std::function<void(Type &)> visit); // write visit, update needed afterwards
if the std::function passed in is value parameter typed, means a read only visit, so no need to update the tree; if std::function is reference typed, the tree will be modified, hence update method need to be called.
I know that I can mark preOrderTraversal method as const and non const overload. However, would it be clear if we know the function pass in can change tree node or not, and overload them.
template<typename Type>
class BinaryTree {
public:
struct Node {
Type data;
Node* left{ nullptr };
Node* right{ nullptr };
Node(Type && value) : data{ std::move(value) } { }
Node() = delete;
};
// ... omit unrealated parts ...
// head, left leaf, right leaf
// value type, tree will not be modified
void preOrderTraversal(std::function<void(Type)> visit) {
std::cout << "void preOrderTraversal(std::function<void(Type)>)" << std::endl;
preorder_utility(visit);
}
// reference type, tree will be modified; so the tree need update order of nodes.
void preOrderTraversal(std::function<void(Type&)> visit) {
std::cout << "void preOrderTraversal(std::function<void(Type&)>)" << std::endl;
preorder_utility(visit);
update(); // update function to keep the tree in binary search tree order.
}
private:
template <typename Function>
void preorder_utility(Function visit) {
if( nullptr == root ) return;
std::stack<Node*> stack;
stack.push(root);
while( !stack.empty() ) {
auto node = stack.top();
stack.pop();
visit(node->data);
if( node->right ) {
stack.push(node->right);
}
if( node->left ) {
stack.push(node->left);
}
}
}
Node* root{nullptr};
};
So that we can have visit function can modify the tree or not.
// just as an example, readonly can not change the tree,
// while write may change the tree
void readonly(double value) {
// value inside tree will not change
value += 10.0;
std::cout << value << std::endl;
}
void write(double & value) {
// value inside tree will change
value += 10.0;
std::cout << value << std::endl;
}
so
int main() {
BinaryTree<double> btree;
// ... working on the tree..., push, pop, etc.
btree.preOrderTraversal(readonly);
btree.preOrderTraversal(write);
}
[update]
It looks like if I make readonly take rvalue-reference, the code will compile and work.
// this will call void preOrderTraversal(std::function<void(Type)> visit)
// hence no update method will be called.
void readonly(double && value) {
// value inside tree will not change
value += 10.0;
std::cout << value << std::endl;
}
Related
I'm creating a GUI Generator for some data that is created from Node classes. Each Node subclass represents a different type of data (e.g. StringNode, IntNode).
At runtime, I don't know the derived types of the nodes. I feel like my only solution is to have a massive list of if statements:
if (auto stringNode = dynamic_cast<StringNode*>(node)) {
/* Generate text input */
} else if (auto intNode = dynamic_cast<IntNode*>(node)) {
/* Generate spin box */
}
// etc...
This seems inefficient and requires careful ordering so derived types are above types they inherit.
Is there another way to do this that uses polymorphism?
The GUI Generator is not in the library where Node is defined.
The GUI for each node may be unique to the derived type (e.g. date picker).
I can't add a virtual generateGui() method on the nodes as this would require generating GUI in the library.
I can edit the Node class.
Node subclasses can be added outside this library and should also allow for their GUI to be generated.
I've looked at Visitor Pattern and the Strategy Pattern but I don't think they will fit as not all classes or GUI generators will be available in the library.
I can offer this implementation:
enum class NodeType { Unknown, String, Int, Float };
using CreateFunc = void(*)(Node *node);
using TypeFunc = bool(*)(Node *node);
// define type-checkers:
bool IsStringNode(Node *node)
{
return dynamic_cast<StringNode*>(node) != nullptr;
}
bool IsIntNode(Node *node)
{
return dynamic_cast<IntNode*>(node) != nullptr;
}
bool IsFloatNode(Node *node)
{
return dynamic_cast<FloatNode*>(node) != nullptr;
}
class GuiGenerator
{
public:
// define specific creators:
static void CreateStringGui(Node *node) { cout << "Creating String Gui" << endl; }
static void CreateIntGui(Node *node) { cout << "Creating Int Gui" << endl; }
static void CreateFloatGui(Node *node) { cout << "Creating Float Gui" << endl; }
//void Create(<Some other node>) {}
};
map<NodeType, CreateFunc> Creators =
{
{ NodeType::String, &GuiGenerator::CreateStringGui },
{ NodeType::Int, &GuiGenerator::CreateIntGui },
{ NodeType::Float, &GuiGenerator::CreateFloatGui }
};
NodeType GetType(Node *node)
{
vector<pair<NodeType, TypeFunc>> types =
{
{ NodeType::String, &IsStringNode },
{ NodeType::Int, &IsIntNode },
{ NodeType::Float, &IsFloatNode }
};
for(auto &a : types)
if(a.second(node))
return a.first;
return NodeType::Unknown;
}
// And here is it: your simplified function
//
static void CreateGUI(Node *node)
{
auto type = GetType(node);
Creators[type](node);
}
And you can use it like this:
Node *node1 = new StringNode();
Node *node2 = new IntNode();
Node *node3 = new FloatNode();
CreateGUI(node1);
CreateGUI(node2);
CreateGUI(node3);
There are no error handling provided for simplification.
I am writing a binary tree search program but I'm not sure how to add nodes and search through them. The nodes come from a .txt file that is being read with a different file so just assume that already works.
The text file looks like:
Name Location
Old Building 31.2222
New Building 21.2111
Like I said, the program already reads in the file so that's not an issue. However, I have to insert the name and location into the nodes of the binary tree. Then I have to search everything within a range which is where the plus minus comes from.
Side note: my copy constructor may be incorrect as well though it complies properly.
Thanks for the help!
#ifndef BINTREE_HPP
#define BINTREE_HPP
#include <utility>
#include <string>
#include <vector>
class bintree {
// A binary search tree for locations in Lineland.
// Notes:
// - Assume a flat, one-dimensional world with locations from -180 to 180.
// - All locations and distances are measured in the same units (degrees).
public:
// Default constructor
bintree() {
this->root = NULL;
}
// Copy constructor
bintree(const bintree &t) {
this -> root = NULL;
*this = t;
}
// Destructor
~bintree() {
}
// Copy assignment is implemented using the copy-swap idiom
friend void swap(bintree &t1, bintree &t2) {
using std::swap;
// Swap all data members here, e.g.,
// swap(t1.foo, t2.foo);
// Pointers should be swapped -- but not the things they point to.
}
bintree &operator= (bintree other) {
// You don't need to modify this function.
swap(*this, other);
return *this;
}
void insert(const std::string& name, double p) {
// insert node with name and location (p)
}
void within_radius(double p, double r, std::vector<std::string> &result) const {
// Search for elements within the range `p` plus or minus `r`.
// Clears `result` and puts the elements in `result`.
// Postcondition: `result` contains all (and only) elements of the
// tree, in any order, that lie within the range `p` plus or minus
// `r`.
}
private:
struct node
{
node *left;
node *right;
};
node* root;
};
#endif
First, your nodes need to hold the data:
struct node
{
node *left;
node *right;
std::string name; // This is the key for your reasearch
double p; // followed by other data
};
Then you can think to browsing through your tree to insert a new node.
In this example, I assume that you can insert several nodes with the same name.
void insert(const std::string& name, double p) {
node *n = new node; // create a new node
n->name=name; n->p=p; // intialise the data payload
n->left=n->right=nullptr; // and make it a leaf.
if (root==nullptr) // if tree is empty,
root = n; // add the new node.
else { // else find where to insert it
node* t=root;
while (true) {
if (t->name > n->name) { // go to left
if (t->left==nullptr) {
t->left = n;
break;
}
else t=t->left;
}
else if (t->name == n->name) { // insert between current and next
n->right = t->right;
t->right = n;
break;
}
else { // go to right
if (t->right==nullptr) {
t->right = n;
break;
}
else t=t->right;
}
}
}
}
Here a live demo.
Note that I have only answered your insertion question, you still have to do a lot on your own (operator= and copy constructor need review, a destructor needs to be created, etc...)
I'm trying to teach myself about classes in C++, and I'm running into a bit of a stumbling block, which I can't seem to clear up. I was hoping someone might be able to point me in the correct direction.
I decided to construct a small Tree class, which constructs a new BST. I want to be able to call certain methods on my object like so:
int main() {
Tree<int> tree1;
tree1.insert(5);
int treeMin = tree1.minValue();
int treeMax = tree1.maxValue();
tree1.printTree();
}
Right now, in order to call these functions, I am defining both public and private functions so that you don't call function in a redundant manner. for instance:
(what I'm trying to avoid)
int main() {
Tree<int> tree1;
tree1.insert(tree1, 5);
int treeMin = tree1.minValue(tree1);
int treeMax = tree1.maxValue(tree1);
tree1.printTree(tree1);
}
In order to do avoid having this redundancy, I am defining a public and private version of the same function. In this way, the public functions call their private counterparts.
template<class T>
class Tree {
private:
treeNode<T>* root;
treeNode<T>* newNode(T data);
void insert(treeNode<T>*& root, T data);
int minValue(treeNode<T>*& root);
int maxValue(treeNode<T>*& root);
void printTree(treeNode<T>*& root);
public:
Tree();
~Tree();
void insert(T data);
int minValue();
int maxValue();
void printTree();
};
And then, as an example:
template<class T>
int Tree<T>::minValue() { minValue(root); }
template<class T>
int Tree<T>::minValue(treeNode<T>*& root) {
if (root == NULL) { return 0; }
if (root->left == NULL) { return root->data; }
else { minValue(root->left); }
}
So, my question is:
If I'm writing my functions recursively, I understand that I need to declare a private function that accepts an argument, but is this considered a bad style? Is this sloppy?
Thanks for your help!
The private member functions in your code are only a needless complication. I would just move their code to the public member functions: less code, more clean code, less indirection so more directly grokable code, all nice. For some of them you might support reuse by making them free functions in a details namespace, but I think that would be premature generalization, expending effort on possible reuse that probably won't take place.
Example code at end of answer.
Re another design issue, declaring
int minValue();
int maxValue();
precludes calling these member functions on a const object. Instead do
int minValue() const;
int maxValue() const;
A third issue, it's generally a Really Bad Idea™ to do i/o in a non-i/o class. If you print the tree to standard output, how would you use the class in a GUI program? So, instead of
void printTree();
do e.g.
ostream& operator<<( ostream& stream ) const;
or e.g.
string toString() const;
A fourth issue, you need to take charge of copying – read up on the “rule of three” and the “rule of zero”.
The simplest way to do that is to replace
treeNode<T>* root;
with
unique_ptr< treeNode< T > > root;
where unique_ptr is std::unique_ptr.
Alternatively declare at least a copy constructor and a copy assignment operator, or inherit from a “non-copyable” class. To make the class effectively non-copyable, you can make these operators private or protected. To make it copyable, make them public and do the right thing in each (a good default implementation of the copy assignment operator is to express it in terms of copy construction via the copy-and-swap idiom, which means introducing a non-throwing swap function).
A fifth issue is that the implementation
template<class T>
int Tree<T>::minValue(treeNode<T>*& root) {
if (root == NULL) { return 0; }
if (root->left == NULL) { return root->data; }
else { minValue(root->left); }
}
strongly suggests that each node stores a value that's implicitly convertible to int. You don't provide the declaration of treeNode. But this looks like a design level bug, that the intent was for minValue to return a T, not an int – and ditto for maxValue.
A very small coding issue (not design level): in C++11 and later you should preferentially use nullptr, not NULL.
nullptr can be freely passed through argument forwarding functions, while NULL then suffers a decay to integral type, since NULL is just a zero-constant of integral type.
nullptr does not require that you include any header, while NULL is defined by a header, i.e. with nullptr you avoid a header dependency.
Finally, regarding
if (root == NULL) { return 0; }
for the minValue, this may of course be the intention, the design. But possibly you want to either signal failure or treat the call as a logic error.
To treat the call as an error, assert( root != nullptr ); and provide a means for the client code to check for empty tree.
To signal failure, either return an object with optional value (e.g. like boost::optional or Barton/Nackmann's original Fallible), or throw an exception (the std::runtime_error class is a good general default exception class choice).
It's also possible to combine the two approaches, to provide both, perhaps with names like minValue and minValueOrX.
More generally it's sometimes possible to reserve some special value as a "no such" indicator. E.g. std::numeric_limits<T>::min(). But this makes for brittle code, since such a value can easily occur naturally in the data, and since client code may easily fail to check for the special value.
Example, coded for C++11:
#include <assert.h>
#include <iostream> // std::cout, std::endl
#include <string> // std::string
namespace my {
using std::string;
template<class T>
class Tree
{
private:
struct Node
{
T value;
Node* p_left;
Node* p_right;
auto to_string() const -> string
{
using std::to_string;
string const left = (p_left == nullptr? "" : p_left->to_string());
string const right = (p_right == nullptr? "" : p_right->to_string());
return "(" + left + " " + to_string( value ) + " " + right + ")";
}
~Node() { delete p_left; delete p_right; }
};
Node* root_;
Tree( Tree const& ) = delete;
Tree& operator=( Tree const& ) = delete;
public:
auto is_empty() const -> bool { return (root_ == nullptr); }
void insert( T const data )
{
Node** pp = &root_;
while( *pp != nullptr )
{
auto const p = *pp;
pp = (data < p->value? &p->p_left : &p->p_right);
}
*pp = new Node{ data, nullptr, nullptr };
}
auto minValue() const -> T
{
assert( root_ != nullptr );
Node* p = root_;
while( p->p_left != nullptr ) { p = p->p_left; }
return p->value;
}
auto maxValue() const -> T
{
assert( root_ != nullptr );
Node* p = root_;
while( p->p_right != nullptr ) { p = p->p_right; }
return p->value;
}
auto to_string() const -> string
{
return (root_ == nullptr? "" : root_->to_string());
}
~Tree() { delete root_; }
Tree(): root_( nullptr ) {}
Tree( Tree&& other ): root_( other.root_ ) { other.root_ = nullptr; }
};
} // namespace my
auto main() -> int
{
my::Tree<int> tree;
for( int const x : {5, 3, 4, 2, 7, 6, 1, 8} )
{
tree.insert( x );
}
using std::cout; using std::endl;
cout << tree.to_string() << endl;
cout << "min = " << tree.minValue() << ", max = " << tree.maxValue() << endl;
}
Output:
(((( 1 ) 2 ) 3 ( 4 )) 5 (( 6 ) 7 ( 8 )))
min = 1, max = 8
Suppose I have a C++ class and I would like to have a recursive member-function which is called with instances items of the class, for example
// the eplicit "this" is just for clarity in the following code:
void recursivePrintTree(){
(if this == NULL){ // We are "out" of the tree
return;
}
cout << this->val;
(this->leftSon)->printBinaryTree();
(this->rightSon)->printBinaryTree();
}
The problem is of course invoking undefined behaviour by calling printBinary with NULL in the first place! so I would like to avoid this, and as far as I know I have at least three ways of doing so:
1) Using static member functions, which get an explicit this-type argument that can be safely checked. this is actually what I did so far but because it's a very recursive implementation, almost all of the member-functions get coded as static. That's not very good, right?
2) checking the stop condition for the next node before having another recursive call with a NULL pointer possibly as "this". This is a much less natural form of writing and actually checks other items other that This. and I would like to avoid it.
3) Using default dummy values. Tried it, felt it's not really saving me any special-case-treatment, but that may have been just because of the Generic-ness of my tree.
I have really been fussing around this matter for a while now so would appreciate any good advice.
Your code is wrong.
Instead of checking for NULL in this, you can check for NULL in this->next so you can avoid calling the method for NULL pointers in the first place.
That is, instead of:
void printBinaryTree() {
if(this == NULL){
return;
}
cout << this->val;
this->next->printBinaryTree();
}
Do this:
void printBinaryTree() {
cout << this->val;
if(this->next)
this->next->printBinaryTree();
}
BTW. this is a linked list.
The second solution is the only solution if you want to
navigate from within the node structure. The usual solution,
however, is to distinguish between nodes and the tree, and the
navigation code is a member of the tree object, not the node.
At most, the node has a function to return the next pointer.
This means that the naviagtion functions would take a pointer to
the nodes; your printBinaryTree might be something like:
void
BinaryTree::print( Node const* node )
{
if ( node != NULL ) {
node->print();
print( node->next() );
}
}
Or you can use the visitor pattern, which separates the tree
walking code from the actions at each node.
Let's try out your implementation:
#include <iostream>
class BinaryTree {
public:
BinaryTree(int value, BinaryTree * left, BinaryTree * right) : value_(value), left_(left), right_(right) {}
void printBinaryTree(int depth = 0) {
for ( int i = 0; i < depth; i++ ) std::cout << " ";
if ( this == NULL ) {
std::cout << "Null node, returning..." << std::endl;
return;
}
else {
std::cout << value_ << std::endl;
}
left_->printBinaryTree(depth+1);
right_->printBinaryTree(depth+1);
}
private:
int value_;
BinaryTree * left_;
BinaryTree * right_;
};
int main() {
BinaryTree leaf(0,NULL,NULL);
BinaryTree top(1,&leaf, &leaf);
top.printBinaryTree();
return 0;
}
If we make this run, we get an output that looks like this:
1
0
Null node, returning...
Null node, returning...
0
Null node, returning...
Null node, returning...
The reason why this works is explained here: Accessing class members on a NULL pointer
However, as per C++ standard, doing this is undefined behaviour. As in, this works only because the implementation of your, or in this case mine, compiler is able to make this work. It is not a guarantee of any kind, and this reduces your portability, and could even stop working if you ever need to update your compiler!
There are a bunch of alternatives to this. You already list some, though I must say I dislike the static implementation because it doesn't make really sense from a design standpoint, and makes all your code a mess. An additional solution could be to make the printBinaryTree function virtual, and define the leaf nodes as a child class of the tree. This is an example:
#include <iostream>
class BinaryTree {
public:
BinaryTree(int value, BinaryTree * left, BinaryTree * right) : value_(value), left_(left), right_(right) {}
virtual void printBinaryTree(int depth = 0) {
for ( int i = 0; i < depth; i++ ) std::cout << " ";
std::cout << value_ << std::endl;
left_->printBinaryTree(depth+1);
right_->printBinaryTree(depth+1);
}
int getValue() { return value_; }
private:
int value_;
BinaryTree * left_;
BinaryTree * right_;
};
class BinaryTreeLeaf : public BinaryTree {
public:
BinaryTreeLeaf(int value) : BinaryTree(value, NULL, NULL) {}
virtual void printBinaryTree(int depth=0) {
for ( int i = 0; i < depth; i++ ) std::cout << " ";
std::cout << getValue() << std::endl;
}
};
int main() {
BinaryTreeLeaf leaf(0);
BinaryTree top(1,&leaf, &leaf);
top.printBinaryTree();
return 0;
}
The output here, as desired, is:
1
0
0
Suppose I want to create an unmodifiable linked-list (i.e. it can only be traversed, no nodes can be added or removed once it was initially created). This could be easily implemented by:
struct ListNode
{
int value;
ListNode* nextNode;
}
My question is .... Would it be possible to use references instead of pointers?
struct ListNodeWithRefs
{
int value;
ListNodeWithRefs &nextNode;
}
I am not sure it would provide any performance gain at all but ... this question popped up while coding and my answer so far is no but I could be missing something.
In principle, nothing prevents you from using references, and constructing list elments like this:
ListNodeWithRefs::ListNodeWithRefs(ListNodeWithRefs &next):
nextNode(next)
{}
But there is a chicken and egg problem because next also enforces its next element to exist at its creation and so on ...
Note: I think my question can also be applied to defining the list as:
struct ListNodeConst
{
int value;
const ListNode* nextNode;
}
This is typical of a cons-list in functional languages:
data List a = Empty | Node a (List a)
The trick is though, List a is a full type and can refer either to Empty OR another node (which is why it can terminate).
In order to achieve this in C++, you could take advantage of either a union (but it's not that well supported) or of polymorphism.
template <typename T>
struct ListBase {
enum class Kind { Empty, Node };
explicit ListBase(Kind k): _kind(k) {}
Kind _kind;
};
And then:
template <typename T>
struct EmptyList: ListBase<T> {
EmptyList(): ListBase<T>(Kind::Empty) {}
};
template <typename T>
struct ListNode: ListBase<T> {
ListNode(T const& t, ListBase<T>& next):
ListBase<T>(Kind::Node), _value(t), _next(next) {}
T _value;
ListBase<T>& _next;
};
And now, you don't have a chicken & egg problem any longer; just start from an instantiation of EmptyList<T>.
Note: the presence of _kind in the base class is not that OO, but it makes things closer to the functional example by tagging which alternative is used.
Take a look at this example by sbi, it seems to work: https://stackoverflow.com/a/3003607/1758762
// Beware, un-compiled code ahead!
template< typename T >
struct node;
template< typename T >
struct links {
node<T>& prev;
node<T>& next;
link(node<T>* prv, node<T>* nxt); // omitted
};
template< typename T >
struct node {
T data;
links<T> linked_nodes;
node(const T& d, node* prv, node* nxt); // omitted
};
// technically, this causes UB...
template< typename T >
void my_list<T>::link_nodes(node<T>* prev, node<T>* next)
{
node<T>* prev_prev = prev.linked_nodes.prev;
node<T>* next_next = next.linked_nodes.next;
prev.linked_nodes.~links<T>();
new (prev.linked_nodes) links<T>(prev_prev, next);
next.linked_nodes.~links<T>();
new (next.linked_nodes) links<T>(next, next_next);
}
template< typename T >
void my_list<T>::insert(node<T>* at, const T& data)
{
node<T>* prev = at;
node<T>* next = at.linked_nodes.next;
node<T>* new_node = new node<T>(data, prev, next);
link_nodes(prev, new_node);
link_nodes(new_node, next);
}
How does the list end?
You will need at least two types: the end and not. You also need lifetime management. And either runtime or static knowledge of which type.
A completely static implementation could be done, where each node is its own type that knows how far it is to the end.
Or you could just have an unintialized buffer, and create elements off it in reverse order.
A circle is also possible. Make the first reference refer to the last element you construct.
No. Reasons:
You cannot insert a node if nextNode is a reference.
What should nextNode refer to if this is list tail?
As #Vlad said, the problem with references is that you will need a final object.
The good news is that, in principle, you can still have a cyclic list, if you have a use for it.
This is a fundamental thing, if the "next" element is a non-nullable reference means that there is always a next element, that is, the list is either infinite or, more realistically, it closes on itself or into another list.
Taken the exercise further is quite interesting and strange.
Basically the only thing that seems to be possible is to defined the equivalent of the a node (which also represents the list).
template<class T>
struct node{
T value; // mutable?
node const& next;
struct circulator{
node const* impl_;
circulator& circulate(){impl_ = &(impl_->next); return *this;}
T const& operator*() const{return impl_->value;}
friend bool operator==(circulator const& c1, circulator const& c2){return c1.impl_ == c2.impl_;}
friend bool operator!=(circulator const& c1, circulator const& c2){return not(c1==c2);}
};
circulator some() const{return circulator{this};}
};
The elements have to live in the stack and the list is static (well, references are not rebindable anyway) and the links have to be const references!
Eventually the value can be made then mutable apparently (probably safe?).
(At this point one wonders how is this different from a stack array references by a modulo indices.)
There is only one way to construct the node/list object, that is to close it with itself (or with other preexising node). So the resulting list are either circular or "rho" shape.
node<int> n1{5, {6, {7, n1}}};
auto c = n1.some();
cout << "size " << sizeof(node<int>) << '\n';
do{
cout << *c << ", ";
c.circulate();
}while(c != n1.some()); //prints 5, 6, 7
I wasn't able to make a node that is not trivially constructible (aggregate?).
(Adding any sort of basic constructor produced segmentation faults for a reason I can't understand, both in gcc and clang).
I wasn't able to encapsulate the node in a "container" object for the same strange reason.
So making an object that could be constructed like this was impossible to me:
circular_list<int> l{1,2,3,4}; // couldn't do this for obscure reasons
Finally, since a proper container cannot be constructed it is not clear what is the semantics of this object, for example when two "lists" are equal? what doesn't mean to assign? or assign between list of different sizes?
It is a quite paradoxical object, with no general value or reference semantics apparently.
Any comments or improvements are welcomed!
I might be off the mark, but this works
struct Node;
struct Node {
using link = std::reference_wrapper<Node>;
Node( char data_ = 0)
: next({*this})
, data( data_ == 0 ? '?' : data_ )
{}
bool is_selfref() const noexcept {
return (this == & next.get());
}
char data;
link next;
};
The usual tests
Node A('A');
Node B('B');
Node C('C');
assert( A.is_selfref() == B.is_selfref() == C.is_selfref());
A.next = B; B.next = C;
assert(! A.is_selfref() && ! B.is_selfref() );
assert( C.is_selfref() );
assert( 'A' == A.data );
assert( 'B' == A.next.get().data );
assert( 'C' == A.next.get().next.get().data );
// C.next == C
// for those who feel safe seeing the END
Node END(127);
C.next = END;
Of course, as long as all Node's stay in the scope we are all ok here. Otherwise not. Strange and wonderful. Very limited utility?
That was quite tricky but this worked :
#include <iostream>
#include <typeinfo>
class Last {
public:
int x;
int last;
Last(int i) {
std::cout << "parent constructor(" << i << ")\n";
x = i;
last = 1;
}
};
struct fourptr {
int v1, v2;
void *p1, *p2;
};
class chain : public Last {
public:
chain(int i) : Last(i) {
std::cout << "child constructor(" << i << ")\n";
last = 0;
}
void viewandnext() {
struct fourptr *fp = (struct fourptr *) this;
std::cout << x << ", this = " << this
<< ", sizeof *this = "<< sizeof * this
<< ", * this = {" << fp->v1 << ", " << fp->v2 << ", "
<< fp->p1 << ", " << fp->p2 << "}"
<< "\n";
if (last == 0) ((chain &)next).viewandnext();
}
Last & fn(int x) {
Last * e = (x>0) ? new chain(x-1) : new Last(x-1);
return *e;
}
Last & next = fn(x); // This is not a pointer but a reference
};
int main(void) {
chain &l = *(new chain(8));
std::cout << "sizeof l = "<< sizeof l << "\n";
l.viewandnext();
}
A simple way to avoid a chicken-egg problem for a list with references is to remember that firstly your object memory is allocated, then your constructor is called. Moreover, access to this pointer is guaranteed inside of a constructor by C++ standard.
Neat way to resolve this:
struct node {
int data;
node& next;
node(node& n, int d): next(n), data(d) {}
};
node tl(tl, 69); // tl is already in the scope!