There is no such thing as a static class. You are describing a class that has only static data that is populated once and used thereafter. You can have as many non-static functions as you want -- both virtual and regular -- as long as they work with just the static member data.
Here's something that seems like what you are looking for.
#include <iostream>
#include <string>
#include <map>
// A base class template that has a virtual function
// to allow derived classes to fill up the map.
template <typename Key, typename Value>
class MyTemplateClass
{
public:
Value at(Key const& key)
{
PopulateEmptyMap();
return myMap.at(key);
}
void PopulateEmptyMap()
{
if(!myMap.empty())
return;
PopulateMap();
}
protected:
virtual void PopulateMap() = 0;
static std::map<Key, Value> myMap;
};
// Define the static member data of the class template.
template <typename Key, typename Value>
std::map<Key, Value> MyTemplateClass<Key, Value>::myMap;
// Define a sub-calss of MyTemplateClass that works
// int as key and string as value.
class MyClass1 : public MyTemplateClass<int, std::string>
{
protected:
virtual void PopulateMap()
{
myMap[1] = "str1";
myMap[2] = "str2";
}
};
// Define a sub-calss of MyTemplateClass that works
// int as key and int as value.
class MyClass2 : public MyTemplateClass<int, int>
{
protected:
virtual void PopulateMap()
{
myMap[1] = 10;
myMap[2] = 20;
}
};
int main()
{
MyClass1 c1;
std::cout << c1.at(1) << std::endl;
std::cout << c1.at(2) << std::endl;
MyClass2 c2;
std::cout << c2.at(1) << std::endl;
std::cout << c2.at(2) << std::endl;
}
Caution:
If you define another derived class that inherits from MyTemplateClass
using int
as key and int
as value, you will see unexpected behavior. There can be only one instantiation of MyTemplateClass<int, int>
.
// This will lead to unexpected behavior. You will get either 10 and 20 in
// the map or 30 and 40. You will not get all four in the map.
// If the behavior of MyClass2 seems sane, the behavior of MyClass3 will seem
// the opposite, or vice versa.
class MyClass3 : public MyTemplateClass<int, int>
{
protected:
virtual void PopulateMap()
{
myMap[1] = 30;
myMap[2] = 40;
}
};
However, you can address that problem using a third parameter to MyTemplateClass
.
#include <iostream>
#include <string>
#include <map>
template <typename Key, typename Value, typename Derived>
class MyTemplateClass
{
public:
Value at(Key const& key)
{
PopulateEmptyMap();
return myMap.at(key);
}
void PopulateEmptyMap()
{
if(!myMap.empty())
return;
PopulateMap();
}
protected:
virtual void PopulateMap() = 0;
static std::map<Key, Value> myMap;
};
template <typename Key, typename Value, typename Derived>
std::map<Key, Value> MyTemplateClass<Key, Value, Derived>::myMap;
class MyClass1 : public MyTemplateClass<int, std::string, MyClass1>
{
protected:
virtual void PopulateMap()
{
myMap[1] = "str1";
myMap[2] = "str2";
}
};
class MyClass2 : public MyTemplateClass<int, int, MyClass2>
{
protected:
virtual void PopulateMap()
{
myMap[1] = 10;
myMap[2] = 20;
}
};
class MyClass3 : public MyTemplateClass<int, int, MyClass3>
{
protected:
virtual void PopulateMap()
{
myMap[1] = 30;
myMap[2] = 40;
}
};
int main()
{
MyClass1 c1;
std::cout << c1.at(1) << std::endl;
std::cout << c1.at(2) << std::endl;
MyClass2 c2;
std::cout << c2.at(1) << std::endl;
std::cout << c2.at(2) << std::endl;
MyClass3 c3;
std::cout << c3.at(1) << std::endl;
std::cout << c3.at(2) << std::endl;
}
That works for me. Hope it works for you.
Update
There are couple of options that you can choose from that don't require creation of objects on the stack.
All static
Members
You can design the classes to make everything accessible through static
member functions.
#include <iostream>
#include <string>
#include <map>
template <typename Key, typename Value, typename Derived>
class MyTemplateClass
{
public:
static Value at(Key const& key)
{
PopulateEmptyMap();
return myMap.at(key);
}
static void PopulateEmptyMap()
{
if(!myMap.empty())
return;
Derived::PopulateMap();
}
protected:
static std::map<Key, Value> myMap;
};
template <typename Key, typename Value, typename Derived>
std::map<Key, Value> MyTemplateClass<Key, Value, Derived>::myMap;
class MyClass1 : public MyTemplateClass<int, std::string, MyClass1>
{
public:
static void PopulateMap()
{
myMap[1] = "str1";
myMap[2] = "str2";
}
};
int main()
{
// Access the data without needing to create an object.
std::cout << MyClass1::at(1) << std::endl;
std::cout << MyClass1::at(2) << std::endl;
// They are also accessible using an object.
MyClass1 c1;
std::cout << c1.at(1) << std::endl;
std::cout << c1.at(2) << std::endl;
}
Singleton Pattern
Another approach that might work for you is use of the singleton pattern.
#include <iostream>
#include <string>
#include <map>
template <typename Key, typename Value, typename Derived>
class MyTemplateClass
{
public:
Value at(Key const& key)
{
PopulateEmptyMap();
return myMap.at(key);
}
void PopulateEmptyMap()
{
if(!myMap.empty())
return;
PopulateMap();
}
protected:
virtual void PopulateMap() = 0;
static std::map<Key, Value> myMap;
};
template <typename Key, typename Value, typename Derived>
std::map<Key, Value> MyTemplateClass<Key, Value, Derived>::myMap;
class MyClass1 : public MyTemplateClass<int, std::string, MyClass1>
{
public:
static MyClass1* instance()
{
static MyClass1 theInstance;
return &theInstance;
}
void PopulateMap()
{
myMap[1] = "str1";
myMap[2] = "str2";
}
private:
// Disallow creation of objects by clients
MyClass1() {}
~MyClass1() {}
};
int main()
{
// Access the data using the instance() interface.
std::cout << MyClass1::instance()->at(1) << std::endl;
std::cout << MyClass1::instance()->at(2) << std::endl;
}