Let's imagine we have several type of elements, and we want to create a
'manager' for every type of them. The manager takes care of the
creation, activation/deactivation and removal for any of the elements
(we assume the user will no create/destroy the instances of these elements
without using the manager.
A very simple example of the code would be something like this:
template <class T>
class NonCachedElementMngr
{
public:
NonCachedElementMngr():
rmCounter(0)
{}
~ NonCachedElementMngr()
{
T* element = 0;
if(mElements.size() > 0)
{
typename std::set<T*>::iterator it;
for(it = mElements.begin(); it != mElements.end(); ++it)
{
element = *it;
element->deactivate();
delete element;
}
}
}
T* create()
{
T* element = new T();
element->activate();
mElements.insert(element);
return element;
}
bool release(T* element)
{
bool ret = false;
typename std::set<T*>::iterator it;
it = mElements.find(element);
if(it != mElements.end())
{
element->deactivate();
delete element;
mElements.erase(it);
ret = true;
}
return ret;
}
private:
std::set<T*> mElements;
int rmCounter;
};
Let's imagine now that, for a subgroup of objects,
apart from the basic operation, we need also to do
some caching. For that subgroup of types, we could
define another 'manager' like this:
template <class T>
class CachedElementMngr
{
public:
CachedElementMngr():
rmCounter(0)
{}
~CachedElementMngr()
{
T* element = 0;
if(mElements.size() > 0)
{
typename std::set<T*>::iterator it;
for(it = mElements.begin(); it != mElements.end(); ++it)
{
element = *it;
element->removeFromCache(); // <<<<<<<<<<<<<< Different line
element->deactivate();
delete element;
}
}
}
T* create()
{
T* element = new T();
element->storeInCache(); // <<<<<<<<<<<<<< Different line
element->activate();
mElements.insert(element);
return element;
}
bool release(T* element)
{
bool ret = false;
typename std::set<T*>::iterator it;
it = mElements.find(element);
if(it != mElements.end())
{
element->removeFromCache(); // <<<<<<<<<<<<<< Different line
element->deactivate();
delete element;
mElements.erase(it);
ret = true;
}
return ret;
}
private:
std::set<T*> mElements;
int rmCounter;
};
As obvious, both managers are exactly the same, except for the
three lines marked as so.
How could I refactor this two templates?
We know at compile time if a specific type will be cacheable or not.
Notice there is also a different line in the destructor.
Any feasible proposal (virtual inheritance, template specialization, SFINAE...) would be very welcome.
Factor out that specific behavior into a policy:
#include <set>
struct cached_tag;
struct noncached_tag;
template<typename Tag>
struct ElementMngrCachePolicy;
template<>
struct ElementMngrCachePolicy<cached_tag>
{
template<typename T>
static void removeFromCache(T* const element) { /*impl...*/ }
template<typename T>
static void storeInCache(T* const element) { /*impl...*/ }
};
template<>
struct ElementMngrCachePolicy<noncached_tag>
{
template<typename T>
static void removeFromCache(T* const) { /*do nothing*/ }
template<typename T>
static void storeInCache(T* const) { /*do nothing*/ }
};
template<typename T, typename CachePolicy>
class ElementMngr
{
typedef std::set<T*> elements_t;
public:
ElementMngr() :
rmCounter()
{ }
~ElementMngr()
{
for (typename elements_t::iterator it = mElements.begin(); it != mElements.end(); ++it)
{
T* const element = *it;
CachePolicy::removeFromCache(element);
element->deactivate();
delete element;
}
}
T* create()
{
T* const element = new T();
CachePolicy::storeInCache(element);
element->activate();
mElements.insert(element);
return element;
}
bool release(T* const element)
{
typename elements_t::iterator it = mElements.find(element);
if (it == mElements.end())
return false;
CachePolicy::removeFromCache(element);
element->deactivate();
delete element;
mElements.erase(it);
return true;
}
private:
elements_t mElements;
int rmCounter;
};
template<typename T>
class CachedElementMngr : public ElementMngr<T, ElementMngrCachePolicy<cached_tag> >
{ };
template<typename T>
class NonCachedElementMngr : public ElementMngr<T, ElementMngrCachePolicy<noncached_tag> >
{ };
Use a policy class...
template <class T, typename Policy>
class ElementMngr
{
~ElementMngr()
{
T* element = 0;
if(mElements.size() > 0)
{
typename std::set<T*>::iterator it;
for(it = mElements.begin(); it != mElements.end(); ++it)
{
element = *it;
Policy::cleanup(element);
delete element;
}
}
}
T* create()
{
T* element = new T();
Policy::init(element);
mElements.insert(element);
return element;
}
bool release(T* element)
{
bool ret = false;
typename std::set<T*>::iterator it;
it = mElements.find(element);
if(it != mElements.end())
{
Policy::release(element);
delete element;
mElements.erase(it);
ret = true;
}
return ret;
}
};
Then define two policies, both implementing the init(), cleanup() and release() methods, but one does the extra line, the other doesn't...
EDIT: fixed my pseudo code so that it's more like real code, I wanted to show that you can make Policy depend on T too, and then use for example specialization for specific T, or you don't have to - you can decide how to define the policy....
You have any number of options, but the basic idea is to add polymorphism.
For runtime polymorphism, you have these general choices:
Strategy Pattern
Store functors defining whether or not to use the cache (i.e. with std::function s)
Template method (which has nothing to do with C++ templates)
You could also use compile time polymorphism, as detailed in Nim's answer
For an example, here is template method:
template <typename T>
class ElementManager
{
std::set<T*> mElements;
int rmCounter;
virtual void CacheStore(T& element) = 0;
virtual void CacheRemove(T& element) = 0;
public:
ElementManager():
rmCounter(0)
{}
~ElementManager()
{
T* element = 0;
if(mElements.size() > 0)
{
typename std::set<T*>::iterator it;
for(it = mElements.begin(); it != mElements.end(); ++it)
{
element = *it;
CacheRemove(element);
element->deactivate();
delete element;
}
}
}
T* create()
{
T* element = new T();
CacheStore(element);
element->activate();
mElements.insert(element);
return element;
}
bool release(T* element)
{
bool ret = false;
typename std::set<T*>::iterator it;
it = mElements.find(element);
if(it != mElements.end())
{
CacheRemove(element);
element->deactivate();
delete element;
mElements.erase(it);
ret = true;
}
return ret;
}
}
template <class T>
class CachedElementMngr : public ElementManager<T>
{
virtual void CacheStore(T& element)
{
element->storeInCache();
}
virtual void CacheRemove(T& element)
{
element->removeFromCache();
}
};
template <class T>
class NonCachedElementMngr : public ElementManager<T>
{
virtual void CacheStore(T& element)
{
//Purposely Empty
}
virtual void CacheRemove(T& element)
{
//Purposely Empty
}
};
Specifically from the example, it seems that class CachedElementMngr completely contains all the functionality of class NonCachedElementMngr except those 3 lines mentioned. I would do something like this:
template<typename T>
class NonCachedElementMngr
{
/* put all content of "CachedElementMngr" in your example and below methods */
virtual void removeFromCache(T* p) { /* empty */ }
virtual void storeInCache(T* p) { /* empty */ }
virtual ~NonCachedElementMngr() { /*retain original */ }
};
template<typename T>
class CachedElementMngr : public NonCachedElementMngr<T>
{
// everything is inherited; just add below methods in this class
virtual void removeFromCache(T* p) { p->removeFromCache(); }
virtual void storeInCache(T* p) { p->storeInCache(); }
virtual ~CachedElementMngr() { /*retain original */ }
};
And intead of calling following method as,
p->removeFromCache();
p->storeInCache();
it should be called as
removeFromCache(p);
storeInCache(p);
Related
I'm writing codes about binary tree using smart pointer, but there is something wrong with destructor. I don't know where the momery leaks. But when I remove all components with parent pointer in BinNode, it runs with no errors.
Header file is showed as follow:
#ifndef BINARY_H
#define BINARY_H
#include <memory>
#include <iostream>
#ifndef RANK_DEFINED
#define RANK_DEFINED
typedef int64_t Rank;
#endif
template <typename T> class BinaryTree;
typedef enum {RB_RED, RB_BLACK} RBColor;
template <typename T>
class BinNode {
public:
friend class BinaryTree<T>;
BinNode() = default;
BinNode(const T& data, std::shared_ptr<BinNode> pare = nullptr, std::shared_ptr<BinNode> left = nullptr, std::shared_ptr<BinNode> right = nullptr,
int h = 1) : _data(data), left_child(left), right_child(right), parent(pare), height(h){}
~BinNode() = default;
std::shared_ptr<BinNode> insertAsLeft(const T& e) {
left_child = std::make_shared<BinNode>(e);
left_child->parent = std::shared_ptr<BinNode>(this);
return left_child;
}
std::shared_ptr<BinNode> insertAsRight(const T& e) {
right_child = std::make_shared<BinNode>(e, this);
return right_child;
}
int size() const;
std::shared_ptr<BinNode> succ();
template <typename VST> void travelLevel(VST (*f)());
template <typename VST> void travelPre(VST (*f)());
template <typename VST> void travelIn(VST&);
template <typename VST> void travelPost(VST&);
bool operator<(BinNode const& bn) { return _data < bn._data; }
bool operator==(BinNode const& bn) { return _data == bn._data; }
bool isRoot() { return !(parent); }
bool isLChild() { return !isRoot() && this == parent->left_child; }
bool isRChild() { return !isRoot() && this == parent->right_child; }
bool hasParent() { return !isRoot(); }
bool hasLChild() { return !left_child; }
bool hasRChild() { return !right_child; }
bool hasChild() { return hasLChild() || hasRChild(); }
bool hasBothChild() { return hasLChild() && hasRChild(); }
bool isLeaf() { return !hasChild(); }
std::shared_ptr<BinNode> sibling() const {
return (isLChild() ? parent->right_child : parent->left_child);
}
std::shared_ptr<BinNode> uncle() const {
return parent->sibling();
}
private:
T _data;
std::shared_ptr<BinNode<T>> left_child = nullptr;
std::shared_ptr<BinNode<T>> right_child = nullptr;
std::shared_ptr<BinNode<T>> parent = nullptr;
int height = 1;
};
// Binary Tree Defination
template <typename T>
class BinaryTree
{
using BN = BinNode<T>;
public:
BinaryTree(): _size(0), _root(nullptr) {}
~BinaryTree() = default;
int size() const { return _size; }
bool empty() const { return !_root; }
std::shared_ptr<BN> root() const { return _root; }
std::shared_ptr<BN> insertAsRoot(const T& e);
std::shared_ptr<BN> insertAsLC(std::shared_ptr<BN> pare, const T& e);
std::shared_ptr<BN> insertAsRC(std::shared_ptr<BN> pare, const T& e);
std::shared_ptr<BN> insertAsLC(std::shared_ptr<BN> pare, BinaryTree<T> bt);
std::shared_ptr<BN> insertAsRC(std::shared_ptr<BN> pare, BinaryTree<T> bt);
int remove(std::shared_ptr<BN>);
BinaryTree* secede(std::shared_ptr<BN>);
template <typename VST>
void travelLevel(VST& visit) { if (_root) _root->travelLevel(visit); }
template <typename VST>
void travelPre(VST& visit) { if (_root) _root->travelPre(visit); }
template <typename VST>
void travelIn(VST& visit) { if (_root) _root->travelIn(visit); }
template <typename VST>
void travelPost(VST& visit) { if (_root) _root->travelPost(visit); }
protected:
Rank _size;
std::shared_ptr<BN> _root;
virtual int updateHeight(std::shared_ptr<BN>);
void updateHeightAbove(std::shared_ptr<BN>);
void delTree(std::shared_ptr<BN>);
};
template <typename T>
std::shared_ptr<BinNode<T>> BinaryTree<T>::insertAsRoot(const T& e)
{
_root = std::make_shared<BN>(e);
_size = 1;
return _root;
}
template <typename T>
std::shared_ptr<BinNode<T>> BinaryTree<T>::insertAsLC(std::shared_ptr<BN> pare, const T& e)
{
auto newNode = pare->insertAsLeft(e);
_size++;
updateHeightAbove(newNode);
return pare->left_child;
}
template <typename T>
std::shared_ptr<BinNode<T>> BinaryTree<T>::insertAsRC(std::shared_ptr<BN> pare, const T& e)
{
}
template <typename T>
void BinaryTree<T>::updateHeightAbove(std::shared_ptr<BN> x)
{
while(x)
{
updateHeight(x);
x = x->parent;
}
}
template <typename T>
int BinaryTree<T>::updateHeight(std::shared_ptr<BN> x)
{
Rank lh = 1, rh = 1;
if (x->left_child)
lh = x->left_child->height;
if (x->right_child)
rh = x->right_child->height;
x->height = (lh > rh) ? lh : rh;
return x->height;
}
#endif
and main function is:
int main()
{
BinaryTree<int> bt;
bt.insertAsRoot(1);
bt.insertAsLC(bt.root(), 2);
cout << bt.size() << endl;
return 0;
}
Result is double free or corruption (out).
There are two problems with your parent links:
Both the downwards and the upwards pointers are std::shared_ptr. This is known as a reference cycle and prohibits your tree from ever being destroyed properly. A common solution is to make the parent pointer a std::weak_ptr, such that it does not count towards keeping the parent alive.
The second problem is hidden in your insertAsLeft: std::shared_ptr<BinNode>(this) will construct a new shared_ptr with refcount 1. This means that you have multiple shared_ptrs pointing to the same block of memory and the first one that drops to refcount 0 frees the block, leaving you with dangling pointers. Luckily for you, C++ has a ready-made solution. Simply inherit from std::enable_shared_from_this and use left_child->parent = shared_from_this(); instead. In a nutshell, this construction allows BinNode to keep track of which shared_ptr owns it.
I am attempting to create an AVL tree with unique pointers. But I am stuck on the elementary part of inserting nodes recursively. This code creates a segmentation fault at the call to the insertNode_ function. I thought that merely passing raw pointers would work, but it does not.
#ifndef AVL_H
#define AVL_H
#include <memory>
#include <iostream>
template<class T>
class AVL
{
public:
template<class K,class V>
struct nodeAVL;
typedef std::unique_ptr<nodeAVL<int,T>> node_ptr;
typedef nodeAVL<int,T> node;
/* Node struct */
template<class K,class V>
struct nodeAVL
{
nodeAVL(const K& key, const V& value):
key_ (key), value_ (value)
{ left = nullptr;right=nullptr;parent=nullptr; }
std::unique_ptr< nodeAVL<K,V> > left, right;
node* parent;
V value()
{ return value_; }
K key()
{ return key_; }
K key(const K& key)
{ key_ = key; }
private:
K key_;
V value_;
};
/* end of Node struct */
AVL()
{ head_=nullptr; };
void insert (const T& value)
{ std::cout<<value<<" inserting\n";
insertNode_ (head_, head_->parent,value); }
std::string print()
{ std::cout<<"print\n";
return print_inOrder_(head_, ""); }
private:
node_ptr head_;
template <class N, class... Args> //allows make_unique in c11
std::unique_ptr<N> make_unique(Args&&... args) {
return std::unique_ptr<N>(new N(std::forward<Args>(args)...));
}
/* recursive insertion */
void insertNode_( node_ptr& current,node* parent, const T& value)
{
std::cout<<"segmentation fault happens here, this line doesnt print\n";
if (current == nullptr){
current = std::move(make_unique<node>(-1,value));
std::cout<<current->value()<<" inserted\n";
if (parent!= nullptr)
std::cout<<"another issue happens here after root insertion\n";
}
else {
if ( current->value() > value )
insertNode_(current->left, current.get(),value);
else if ( current->value() < value )
insertNode_(current->right, current.get(),value);
}
}
/* recursive inOrder print */
std::string print_inOrder_(const node_ptr& current,std::string print)
{
if (current != nullptr) {
std::cout<<"<"<<current->value()<<">"<<std::endl;
print+= "[" + std::to_string(current->value())+"]";
print = print_inOrder_(current->left,print);
print = print_inOrder_(current->right,print);
}
return print;
}
};
#endif
hello I have a problem with a template. I want a pointer stack using templates but I get the error "does not name a type". I have two classes, one that manages a stack of nodes and the other one is a node class. The problem is in the node class and I don't know how to resolve it. Could somebody explain to me how I should create a node object using a template decleared in another class. The code is below.
template<class T> class PointerStack
{
public:
PointerStack();
bool isEmpty();
bool push(T dataIn);
bool pop();
bool top(T &topItem);
void clear();
void print();
private:
int counter;
Node<T>* start;
};
template<class T>
class Node
{
public:
Node(T dataIn);
Node(T dataIn, Node vorigeIn);
T getData();
Node* getPrevious();
private:
T data;
Node* previous;
Node* next;
};
template<class T>
PointerStack<T>::PointerStack()
{
counter == 0;
}
template<class T>
bool PointerStack<T>::isEmpty()
{
if(counter == 0)
{
return true;
}
else
{
return false;
}
}
template<class T>
bool PointerStack<T>::push(T data)
{
if(isEmpty())
{
start = new Node<T>(data);
counter++;
return true;
}
else
{
Node<T> dummy = start;
start = new Node<T>(data, dummy);
counter++;
return true;
}
}
template<class T>
bool PointerStack<T>::pop()
{
if(isEmpty())
{
return false;
}
else
{
Node<T> dummy = start;
start = start->vorige;
counter--;
delete dummy;
return true;
}
}
template<class T>
bool PointerStack<T>::top(T &topItem)
{
if(isEmpty())
{
return false;
}
else
{
topItem = start.getData();
return true;
}
}
template<class T>
void PointerStack<T>::clear()
{
while(isEmpty())
{
pop();
}
}
template<class T>
void PointerStack<T>::print()
{
Node<T> dummy = start;
if(!isEmpty())
{
for(int i = 0; i < counter; i++)
{
std::cout<<dummy->getData();
dummy->v;
}
}
}
template<class T>
Node<T>::Node(T dataIn)
{
data = dataIn;
previous = NULL;
next = NULL;
}
template<class T>
Node<T>::Node(T dataIn, Node previousIn)
{
data = dataIn;
previous = previousIn;
next = NULL;
previousIn->volgende = this;
}
template<class T>
T Node<T>::getData()
{
return data;
}
template<class T>
Node<T>* Node<T>::getPrevious()
{
return previous;
}
and this is the error message:
You PointerStack class doesn't know of the Node class. You need to forward declare your Node class before declaring PointerStack:
template<class T>
class Node;
/* Your code goes here */
I have an interface ICollection implementing a collection ArdalanCollection like this:
template <typename T>
class ICollection
{
public:
virtual void add(T*) = 0;
virtual T* get(int) = 0;
virtual int count() = 0;
};
template <typename T>
class ArdalanCollection :public ICollection<T>
{
public:
ArdalanCollection() {
index = 0;
};
virtual void add(T* obj) {
encapsolateImplementation.insert(make_pair(index++, obj));
};
virtual T* get(int index) {
return encapsolateImplementation[index];
};
virtual int count() {
return encapsolateImplementation.size();
};
private:
int index;
unordered_map < int, T* > encapsolateImplementation;
};
what I want is to have a generic iterator in ICollection interface which can loop all over the internal container elements(I haven't decided to choose unordered_map as my internal container I might change it to boost or something else). I want to use it in this way:
Node *node1 = new Node(1, 0, 0, 0);
Node *node2 = new Node(1, 0, 0, 0);
ICollection<Node> *nodes = new ArdalanCollection<Node>();
nodes->add(node1);
nodes->add(node2);
for (it=nodes->iterator.begin(); it < nodes->iterator.end(); it++) {
}
First your for loop idiom is not correct. It should rather look like
for(auto it = nodes->begin(); it != nodes->end(); it++)
then something along:
template <typename T, typename MyMap>
class ICollection
{
public:
typedef typename MyMap<int, T *>::iterator iterator;
virtual void add(T*) = 0;
virtual T* get(int) = 0;
virtual int count() = 0;
};
should be fine.
i'm trying to learn how to implement iterator functionality in my own container class so i can use something like std::sort()
i've created my own container class. how do i now add iterator functionality, like begin(), end(), ...
template <class T> class MyVector { public:
MyVector() : m_DataPtr(NULL), m_nSize(0), m_nAllocated(0) {}
~MyVector() { delete m_DataPtr; }
size_t Size() const { return m_nSize; }
void PushBack(const T &data);
private:
T *m_DataPtr;
size_t m_nSize;
size_t m_nAllocated; };
//******************************
template <class T> void MyVector<T>::PushBack(const T &data) {
if (m_nSize == m_nAllocated)
{
m_nAllocated = (m_nAllocated+1) *2;
T *tmp = new T[m_nAllocated];
if (!tmp)
return;
// transfer data from ptr to tmp
for (size_t i = 0; i < m_nSize; i++)
tmp[i] = m_DataPtr[i];
// delete[] ptr and set it to tmp
delete[] m_DataPtr;
m_DataPtr = tmp;
}
m_DataPtr[m_nSize++] = data;
}
You may implement begin() and end() by adding this to your class definition.
T* begin() { return m_DataPtr; }
T* end() { return m_DataPtr + m_nSize; }
Your use of contiguous memory allows raw pointers to function as iterators.