private and public functions - c++

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

Related

Overload functional parameter for in-place modification or not

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;
}

Overloading < operator in template

I'm programming a binary tree class template. When I add a new node I check if the new one is less or greater than current.
I overloaded < and > operator in the class type (Object) and works properly but the template doesn't call the overloaded operator, instead uses the generated by the compiler.
object.cc - Comparison between objects works outside the template
bool Object::operator<(const Object& par_other) const
{
printf("\n <");
return id_ < par_other.id_; //assume that you compare the record based on a
}
EDIT 1: Added some requested code. Thx for the help :)
struct Node {
T* value;
Node* left;
Node* right;
};
template <class T>
void BinaryTree<T>::add(T* par_T, Node* par_node) {
if (par_node == nullptr) {
par_node->left = nullptr;
par_node->value = par_T;
par_node->right = nullptr;
} else {
if (par_node->value == nullptr) {
par_node->value = par_T;
} else if (par_node->value > par_T) {
if (!par_node->right) {
par_node->right = createNode();
}
add(par_T, par_node->right);
} else if (par_node->value < par_T) {
if (!par_node->left) {
par_node->left = createNode();
}
add(par_T, par_node->left);
}
}
Why does a node have a T* instead of a T?
If you have a good reason to do that, then compare with:
*par_T < *(par_node->value)
and
*(par_node->value) < *par_T
Notice the use of * and notice I switched the sides rather than misuse >
If you didn't have a good reason to have a node contain a T* then get rid of the *s in this code (and correspondingly elsewhere) but still remember not to use >, ==, != etc. They all can be inferred by results of < ( a is "equal to" b when both a<b and b<a are false)
You also need to fix more things than you asked about. You seem generally confused about the nature of pointers. An extreme example from your code:
if (par_node == nullptr) {
par_node->left = nullptr;
par_node->value = par_T;
par_node->right = nullptr;
}
Think about what that code is doing!

Checking for this == NULL in a member-function without invoking undefined behaviour

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

Linked-list in C++ using references instead of pointers

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!

Why is this class abstract?

I'm trying to create an implementation of a LinkedList that represents polynomials. The Linked list is going to be a list of "Term"s. A term is an implementation of Data (which is an abstract class with methods: compareTo() and toString()). The Polynomial class has a variable called head which I am trying to initialize as a Term. My compiler is saying that I "cannot declare member of abstract type: Term", but I did not think Term was abstract because it is an implementation of Data (the abstract class). If you guys could take a look at this and let me know of any huge red flags I'm missing, I would greatly appreciate it.
Collection.h:
class Data {
public:
virtual ~Data() {}
virtual int compareTo(Data * other) const = 0;
virtual string toString() const = 0;
};
class Term : public Data {
public:
int coefficient;
string variable1;
int exponentX;
string variable2;
int exponentY;
Term * next;
Term(int coeff, string var1, int exp1, string var2, int exp2, Term * next) :
coefficient(coeff),
variable1(var1),
exponentX(exp1),
variable2(var2),
exponentY(exp2),
next(next) {};
string convertInt(int number) {
stringstream ss;//create a stringstream
ss << number;//add number to the stream
return ss.str();//return a string with the contents of the stream
}
int compareTo(Term * term) {
if(this->exponentX > term->exponentX) {
return 1;
}
else if(this->exponentX < term->exponentX) {
return -1;
}
else {
if(this->exponentY > term->exponentY) {
return 1;
}
else if(this->exponentY < term->exponentY) {
return - 1;
}
else {
return 0;
}
}
}
string toString() {
stringstream s;
int * current = &this->coefficient;
if(*current == 1 || *current == -1) {
}
else if(coefficient != 0) {
s << convertInt(coefficient);
}
else { return s.str(); }
if(variable1 != "" && this->exponentX != 0) {
s << variable1;
s << convertInt(exponentX);
}
if(variable2 != "" && this->exponentY != 0) {
s << variable2;
s << convertInt(exponentY);
}
return s.str();
}
};
Also, here is implementation of LinkedList. There are some other methods in there, but they don't seem to be giving any issues.
LinkedList.cpp:
class Polynomial : public LinkedList {
public:
Term head;
Polynomial() {
this->head = NULL;
}
~Polynomial() {
Term * current = head;
while (current != NULL) {
Term * next = current->next;
delete current;
current = next;
}
}
Thank you!
When you are overriding virtual methods, you have to match the function signatures precisely. The return type may vary in accordance with covariance rules, but the parameter types must be exactly the same.
In the base class Data function compareTo is declared as
virtual int compareTo(Data * other) const
In the derived class Term it is declared as
int compareTo(Term * term)
Firstly, the parameter type is different. Secondly, the const is missing.
This means that you wrote a completely unrelated function in the derived class. It does not override the base class's pure virtual function. Since the base pure virtual function remain non-overriden, class Term is still abstract.
In Term you have to declare your function precisely as
int compareTo(Data * other) const
I assume that you expect to use compareTo in Term only for Term-to-Term comparisons. But in this design you'll have to either receive Data as an argument and then cast it to Term, or use the double-dispatch technique.
P.S. On top of that you declare a Term object as a member head of your Polynomial class and then later use it as if it is a pointer
Term * current = head;
This makes no sense at all. If you want your head to be a pointer, declare it as a pointer. If you want it to be an object, then stop using it as a pointer. Either this or that.