encapsulation and friend classes in C++ - c++

I'm studying C++ container and iterators and I'm trying to implement a rudimental linked list, just to get the hang of thi inner workings of iterators and container related stuff.
I defined a node class, along with a list class and the associated iterator, every class in its own header file,and each header is implemented in a separated code file.I didn't care about friend declarations,that was something I intended to cater to while trying to compile the project, but I stumbled upon something I failed to understand.
I defined several private fields in each class,and I expected the compiler to throw some errors at me during compilation, but it seems to be just fine with that! can you explain where am I wrong?? here's the code:
the node class:
template <typename T>
class mylist_node {
public:
mylist_node(const T&,mylist_node<T>*);
~mylist_node() {}
private:
T element;
mylist_node<T> *next;
};
the list class:
template <typename T>
class mylist {
public:
typedef mylist_iterator<T> iterator;
mylist() : head(NULL),tail(NULL) {}
void push_back(const T&);
bool empty();
iterator begin();
iterator end();
private:
mylist_node<T> *head,*tail;
};
the list implementation code:
#include <cstdlib>
#include "mylist_node.h"
#include "mylist_iterator.h"
#include "mylist.h"
template <typename T>
void mylist<T>::push_back(const T& element)
{
//dynamically allocated object so it is not destroyed on function exit
mylist_node<T> *new_node=new mylist_node<T>(element,NULL);
if (head==NULL)
head=new_node;
else
tail->next=new_node;
tail=new_node;
}
template <typename T>
bool mylist<T>::empty()
{
return head==tail;
}
template <typename T>
typename mylist<T>::iterator mylist<T>::begin()
{
return mylist_iterator<T>(head);
}
template <typename T>
typename mylist<T>::iterator mylist<T>::end()
{
return mylist_iterator<T>(NULL);
}
and the iterator class:
template <typename T>
class mylist_iterator {
public:
T &operator*();
const mylist_iterator<T> &operator++();
bool operator!=(const mylist_iterator<T>&);
private:
mylist_iterator(mylist_node<T> *pointee) : pointee(pointee) {}
mylist_node<T> *pointee;
};
obiouvsly the mylist<T>::push_back() and the overloaded operators in mylist_iterator all access the private fields in mylist_node.
I separatedly compile the source files without the compiler complaining about anything at all!
There must be something I didn't fully understand..
thanks!

Compilers typically don't have a way to "sort of" compile code to figure out errors that would occur regardless of the type. They need to actually compile the code with a real type to detect most errors. If you never instantiate any of these classes, the real compilation won't take place.

Related

Different sort methods with template collection

I sorry if similar question already exists on this forum, if you could, give me the link.
I have a template class
template<typename type>
class DoublyLinkedList {};
And I want to have Sort method in it.
template<typename type>
class DoublyLinkedList
{
public:
void Sort(){}
};
But list is template so it can contains different types. So how I can create methods for all types that I foresee? I tried in this way:
template<typename type>
class DoublyLinkedList
{
public:
void DoublyLinkedList<int>::Sort(){}
void DoublyLinkedList<string>::Sort(){}
};
But it's wrong. Please help.
The way the standard library generally deals with this problem is to allow users to specify their own compare functions. You can add a templated parameter that users of your type can use to provide a Compare function, like std::sort does. In the implementation of sort, you assume that comparer is a function that compares two elements of the list and returns rather or not the first should be before the second.
#include <string>
template<typename type>
class DoublyLinkedList
{
public:
template<class Comp>
void Sort(Comp comparer);
};
void foo(DoublyLinkedList<std::string> & list)
{
// Sort list by length of strings
list.Sort([](const std::string p_left, const std::string p_right){
return p_left.size() < p_right.size();
});
}

How to use templates with Nested class in C++

I am trying to use templates for a nested class. I am not sure how to access the class type of the inner class from another class.
Sample Code below.
// I have a List class that can accept any type. It has an inner class
template <class T>
class List
{
public:
class Node
{
public:
T data;
Node* next;
Node* prev;
};
void addElement(Node& value);
private:
Node* head;
};
// Here I am making an array of Lists
template <class T>
class ListArray
{
public:
// Here is my question.
void add(Node& value); // How to give "Node" class type here ?
private:
List<T> _listArr[10];
};
// Is the below the right way to define ListArray::add, especially the way in which Node type can be passed to it ?
template <class T>
void ListArray<T>::add(List<T>::Node& value)
{
// Make a call to List::addElement and pass Node& value
_listArr[0].addElement(value);
//....
}
Could you kindly let me know how the above can be achieved ? Thanks.
Node is a nested type of a class template:
template <class T>
class ListArray
{
public:
typedef typename List<T>::Node Node_type;
void add(Node_type& value); // Refer to it as Node_type
private:
List<T> _listArr[10];
};
And:
template <class T>
void ListArray<T>::add(typename ListArray<T>::Node_type& value)
{
_listArr[0].addElement(value);
//....
}
I used typedef to define local name for node type. It is very useful - now, clients of ListArray can write code, that uses Node_type explicitly (without knowing what it actually is). This technique is used heavily in std library - usually, std:: types have tons of typedefs to allow writing flexible code.
Also, note the typename keyword - it is required in case of nested types of class templates. It indicates, that given name is the name of a type (without it, you should get a compiler error).

vector of template struct

using namespace std;
#include <vector>
#include <string>
template <class T>
struct ValNode {
string id;
T value;
};
class ValTable {
public:
ValTable();
template <class T>
void add(string,T);
const bool find(string);
void remove(string);
private:
template<class T>
std::vector<ValNode<T>*> vals;
};
complier error:error: data member 'vals' cannot be a member template
i did try to use T* value in the struct, but i didnt work out.
I didnt use any of the functions in codes yet. was just trying to compling it into *.o file (with .cpp file also).
As the error says, variables (including data members) can't be templates; only classes and functions can be.
It looks like you want the table to be able to hold values of various different types, specified at run-time according to which types are passed to add(). For that, you need dynamic types, which aren't directly supported in C++. You might consider libraries like Boost.Any or Boost.Variant for that.
On the other hand, maybe you just want to store a single type in each table, and different types in different tables. In that case, the class itself will need to be a template:
template <typename T>
class ValTable {
public:
ValTable();
void add(string,T);
const bool find(string);
void remove(string);
private:
std::vector<ValNode<T>*> vals;
};
In C++ you can have template methods in a class, but not template data members.
For example:
template<typename T, int n>
struct FixedVector {
T x[n];
FixedVector() {
for (int i=0; i<n; i++) x[i] = 0;
}
template<typename C>
void copy(const C& container) {
if (container.size() != n) {
throw std::runtime_error("Wrong size");
}
int j = 0;
for (typename C::const_iterator i=container.begin(),
e=container.end();
i!=e;
++i)
{
x[j++] = *i;
}
}
};
With the above class you can declare FixedVector<int, 5> f and call f.copy(v) where v can be for example a vector or a list or anything that has size begin and end.
So FixedVector::copy is a template method and this means that the compiler will generate a different version of it for each different type you will pass to the function.
std::vector<double> y;
y.push_back(3.4); y.push_back(5.6); y.push_back(7.8);
std::list<unsigned char> z;
z.push_back('a'); z.push_back('b'); z.push_back('c');
FixedVector<int, 3> v;
v.copy(y); // This is ok
v.copy(z); // This is ok too
C++ doesn't allow template data members because this would imply a different class size depending on how many types you are using in a specific compilation unit and this doesn't go with the C++ compilation model of one-unit-at-a-time.
Adding methods is instead fine because it doesn't affect class size, and everything can be fixed at link time by avoiding pulling multiple copies of the same method from different compilation units.
You will have to declare ValTable as template
template <class T>
class ValTable{
public:
ValTable();
//template <class T>
void add(string,T);
const bool find(string);
void remove(string);
private:
//template<class T>
std::vector<ValNode<T>*> vals;
};
You won't be able to do that ValTable needs to be a template
You can have this :
template <class T> //Make the class as template
class ValTable {
public:
ValTable();
template <class X>
void add(string,X);
const bool find(string);
void remove(string);
private:
//template<class T>
std::vector<ValNode<T>*> vals;
};
You cannot have template member values: each translation unit could access different instantiations resulting in different ibject layouts. You'd need to factor out the type in some way.
The standard library does something along the lines of what you want for std::locale: each std::locale stores a collection of differently typed objects. It is soecial purpose and can't be used for your purpose directly.
The basic idea is to automatically map each type used to an int which is then used to map the type to an instance. The vals member would then be a function template looking up the correct instance. A rough outline could look like this:
int type_index_alloc() {
static std::atomic<int> current(0);
return ++current;
}
template <typename T>
int type_map() {
static int rc = type_index_alloc();
}
class container {
struct base { virtual ~base() {} };
template <typename T>
struct value: base { T v; };
std::map<int, std::shared_ptr<base>> vals_;
public:
T& vals() {
std::shared_ptr<base>& rc(vals_[type_map<T>()]);
if (!rc) {
rc = std::make_shared<value<T>>()); }
return static_cast<value<T>&>(*rc).v;
}
};
This a just trying to show how things are being set up: I currently don't have access to a compiler. Also, the code example just provides access to an object of type T but it could easily be changed to use a std::vector<T> instead.

C++ Derived class

I have a class (Queue) which inherits from a class named Stack.
it goes like this:
template <class T> class Stack
{
public:
virtual const T pop();
LinkedList<T> lst;
};
template <class T> class Queue : public Stack<T>
{
public:
virtual const T pop();
};
template <class T> const T Queue<T>::pop()
{
const T val = lst[0];
return val;
}
The compiler says "lst undecleared"...why?
Because lst is a member of the base class Stack<T> which is a dependent type on T. The compiler can't check dependent types until the template is fully instantiated. You have to let the compiler know that lst is part of such base class by writing Stack<T>::lst.
As its mention in comments, this->lst is also a viable solution. However, people are likely to remove the this as seen unnecessary. Stack<T>::lst seems more explicit in this way.
Try this->lst instead of lst.

am i implementing this template class correctly?

Okay, I'm trying to implement a templated class of an array-based queue called Queue.
Here's how I did it. First, is this the right way to implement a templated class?
The problem is, when I try to compile, I get the error messages
undefined reference to
'Queue::Queue()'
undefined reference to
'Queue::~Queue()'
Any ideas what's wrong? Am I declaring it wrong or implementing it wrong?
queue.h
#ifndef __QUEUE_H__
#define __QUEUE_H__
template <class T>
class Queue
{
int first;
T* array;
public:
Queue();
~Queue;
void enqueue(T next);
T dequeue();
bool is_empty();
bool is_full();
};
#endif
queue.cpp
#include "queue.h"
template <class T> Queue<T>::Queue(){
...
}
template <class T> Queue<T>::~Queue(){
...
}
template <class T> void Queue<T>::enqueue(T next){
...
}
template <class T> T Queue<T>::dequeue(){
...
}
template <class T> bool Queue<T>::is_empty(){
...
}
template <class T> bool Queue<T>::is_full(){
...
}
main.cpp
#include "queue.h"
#include <iostream>
using namespace std;
int main(){
Queue<int> test;
return 0;
}
Several issues:
The cause of your problem - C++ does not really support splitting templates into .h and .cpp files - you need to put everything in the header
The name __QUEUE_H__ is reserved for the C++ implementation, as are all names that contain double-underscores or begin with an underscore and an uppercase letter. You are not allowed to create such names in your own code.
You probably will find it more convenient to implement the queue in terms of a std::deque, rather than a C-style array
I assume you are doing this as a learning exercise, but if not you should know that the C++ Standard Library already contains a std::queue template class.
A template is really just a fancy form of macro that the compiler is aware of, for nearly every implementation of C++. The definitions have to be present so that the compiler can generate code in place of the template.