I need to keep track of all objects created of a specific class and I need to access them using a identifier string. The following code does pretty much exactly what I need. The class NamedObject has a static member m_object_by_name, which maps the name (string) to the object, the constructor adds every created object to the map and the destructor removes deleted objects from the map.
#include <map>
#include <string>
#include <iostream>
using namespace std;
class NamedObject
{
public:
static NamedObject *object_by_name(const string &name) {
return m_object_by_name[name];
}
NamedObject(const string &name) : m_name(name) {
m_object_by_name[m_name] = this;
}
~NamedObject() {
m_object_by_name.erase(this->m_name);
}
const string &name() const{
return m_name;
}
private:
string m_name;
static map<string, NamedObject *> m_object_by_name;
};
map<string, NamedObject *> NamedObject::m_object_by_name;
int main ()
{
new NamedObject("name1");
new NamedObject("name2");
NamedObject *obj1 = NamedObject::object_by_name("name1");
NamedObject *obj2 = NamedObject::object_by_name("name2");
cout << obj1->name() << endl;
cout << obj2->name() << endl;
}
Now I have several classes whose objects need to be accessed by their name. Inheriting form the above NamedObject class of course has the problem that all these classes would share their names (e.g., I cannot have two objects of different classes but with the same name), as they share the map m_objects_by_name. Moreover, when accessing objects using the object_by_name() method, I always have to cast from NamedObject to the actual class.
The solution for this problem I use at the moment can be seen in the following code. However, I am not really satisfied with this solution (see comments below). The template class NamedObjectStore is now responsible for storing all objects of class T. Moreover, there is a base class handling the properties of having a name and a derived class that is really used. The derived class has a static NamedObjectStore object where it adds its objects on creation and removes them on deletion.
#include <map>
#include <string>
#include <iostream>
using namespace std;
template <class T>
class NamedObjectStore
{
public:
void add_object(T *obj) {
m_object_by_name[obj->name()] = obj;
}
void rem_object(T *obj) {
m_object_by_name.erase(obj->name());
}
T *object_by_name(const string &name) {
return m_object_by_name[name];
}
private:
map<string, T *> m_object_by_name;
};
class BaseNamedObject
{
public:
BaseNamedObject(const string &name) : m_name(name) {
}
const string &name() const {
return m_name;
}
private:
string m_name;
};
class DerivedNamedObject : public BaseNamedObject
{
public:
static NamedObjectStore<DerivedNamedObject> store;
DerivedNamedObject(const string &name) : BaseNamedObject(name) {
store.add_object(this);
}
~DerivedNamedObject() {
store.rem_object(this);
}
};
NamedObjectStore<DerivedNamedObject> DerivedNamedObject::store;
int main ()
{
new DerivedNamedObject("name1");
new DerivedNamedObject("name2");
DerivedNamedObject *obj1 = DerivedNamedObject::store.object_by_name("name1");
DerivedNamedObject *obj2 = DerivedNamedObject::store.object_by_name("name2");
cout << obj1->name() << endl;
cout << obj2->name() << endl;
}
On the positive side, the implementation of what makes an object a named object (i.e., the name()-function) is done in the base class BaseNamedObject. Also the implementation of the structure storing all objects lies in the NamedObjectStore class and is hidden behind its methods. This allows me to easily change both of these implementations if desired, without touching all the derived classes.
On the negative side, I still have to type the same stuff over and over again. More precisely, for every derived class (like DerivedNamedObject), I have to declare and to define the static member store, I have to add objects to the store in the constructor and remove them from the store in the destructor.
So here comes my question: is there a nicer way to solve this problem? Or do I just have to live with these four lines of code in every derived class?
Hoping for some inspiring suggestions :-)
Thomas
As stated in my comment, you can solve this using the curiously recurring template pattern. The code below uses your original example, templated on the type actually being stored:
#include <map>
#include <string>
#include <iostream>
using namespace std;
template<class T>
class NamedObject
{
public:
static NamedObject *object_by_name(const string &name) {
return m_object_by_name[name];
}
NamedObject(const string &name) : m_name(name) {
m_object_by_name[m_name] = this;
}
virtual ~NamedObject() {
m_object_by_name.erase(this->m_name);
}
const string &name() const{
return m_name;
}
private:
string m_name;
static map<string, NamedObject *> m_object_by_name;
};
template <class T>
map<string, NamedObject<T> *> NamedObject<T>::m_object_by_name;
class A : public NamedObject<A>
{
public:
A(const std::string& name) : NamedObject(name)
{}
};
class B : public NamedObject<B>
{
public:
B(const std::string& name) : NamedObject(name)
{}
};
int main()
{
new A("Test");
new B("Test");
auto one = A::object_by_name("Test");
auto two = B::object_by_name("Test");
cout << one << " - " << one->name() << "\n";
cout << two << " - " << two->name() << "\n";
delete two;
delete one;
}
Related
I want to make an abstract class, A that will be subclassed by Class B and Class C such that they will all use the same methods in the defined abstract class (B and C are A-able classes).
I have another class, Z, that will contain an array of A-able classes. I would like for it to have a function that allows it to swap between B and C in that array (ie. calling initializer/member function with an argument).
The below example, while not being exactly like what I'm describing above (not using abstract classes), showcases the same issue I'm running into: I'm unable to set the array to the correct subclass, since it's complaining that it was initialized as the parent class.
However, this should be possible to do right? What am I missing here?
#include <iostream>
#include <array>
class BaseItem {
protected:
std::string name;
BaseItem(const std::string & name) : name(name) {};
virtual void printName();
virtual ~BaseItem() = default;
};
class Item1: public BaseItem {
public:
using BaseItem::name;
Item1() : BaseItem("Book1") {}
void printName() {
std::cout << "1" << name;
}
};
class Item2: public BaseItem {
public:
using BaseItem::name;
Item2() : BaseItem("Book2") {}
void printName() {
std::cout << "2" << name;
}
};
class Library {
public:
std::array<BaseItem, 2> books;
void setToItem2() {
for (size_t i = 0; i < books.size(); i++) {
books[i] = new Item2();
}
}
void setToItem1() {
for (size_t i = 0; i < books.size(); i++) {
books[i] = new Item1();
}
}
void printBooks() {
for (auto& entry: books) {
entry->printName();
}
}
};
int main() {
Library a;
a.setToItem1();
a.printBooks();
a.setToItem2();
a.printBooks();
return 0;
}
Edit: Cleaned up a bit, also adding error message below:
prog.cpp: In member function ‘void Library::setToItem2()’:
prog.cpp:36:31: error: no match for ‘operator=’ (operand types are ‘std::array<BaseItem, 2>::value_type’ {aka ‘BaseItem’} and ‘Item2*’)
Edit2: Made the example code more representative of what I want to implement, utilizing code help from some of the existing answers.
Current potential solutions:
Evict books and pass in the correct subclass. This is currently what I'm going with. Just don't know if there is anything that can make this look cleaner (ie. all the casting looks a bit messy).
Make books a variant. The code looks cleaner here, but if I'm to extend to Item3, Item4, etc. I'll have to increase the variant to include all those subtypes, which IMHO defeats part of the purpose of making this "interface" (of course, we still get to inherit some shared things, but I'd like to not have to keep adding new classes into variant).
For now, I'm going to just do 1. But please let me know if there is something better.
Like other comments, if you store a vector of superclass by value, say vector<A>, as the vector allocates the memory, in addition to other information that vector stores, it will allocate sizeof(A)*NumOfElement(vector<A>) for storage. As subclasses, say B need more space than A, object slicing will occur. My suggestion is, instead of storing the class as value, store those as reference. ex)vector<shared_ptr<A>>. As the size of the pointer is same, this will allow to store A's subclasses. Oh, do not forget to define its virtual destructor!
Suggested code:
#include <iostream>
#include <vector>
#include <memory>
class Item {
public:
Item() : name("Book1") {}
std::string name;
virtual void f1() {/* Your Implementation here or make it pure virtual */};
virtual ~Item() = 0;
};
class Item2 : public Item {
public:
Item2() { name = "Book2"; }
//std::string name; //Hides base class name
void f1() override {/* Your Implementation here */};
~Item2() = default;
};
class Library {
public:
std::vector<std::shared_ptr<Item>> books;
void setToItem2() {
books.emplace_back(std::dynamic_pointer_cast<Item>(std::shared_ptr<Item2>(new Item2()))); //If you wish, use loop here
books.emplace_back(std::dynamic_pointer_cast<Item>(std::shared_ptr<Item2>(new Item2())));
}
void printBooks() {
for (auto& entry : books) {
std::cout << entry->name;
}
}
};
int main() {
Library a;
a.printBooks();
return 0;
}
The blessed way to store polymorphic instances in a container is to use std::unique_ptr. The container is still the sole owner of the object, but that pattern does not suffer the object slicing problem.
Furthermore your class hierarchy is weird: an Item2 instance will contain two versions of name. One (not directly accessible) in its Item base class and one directly accessible. It should at least be:
class Item2 : public Item {
public:
using Item::name;
Item2() {
name = "Book2";
}
};
But at construction time, name will first receive "Book1" at the base class initialization time, and then "Book2". So the normal way would be to build a base class like:
class BaseItem {
protected:
std::string name;
BaseItem(const std::string & name) : name(name) {};
virtual ~BaseItem() = default;
};
class Item: public BaseItem {
public:
using BaseItem::name;
Item() : BaseItem("Book1") {}
};
You can now build your Library class:
class Library {
public:
std::array<std::unique_ptr<BaseItem>, 2> books;
void printBooks() {
for (auto& entry : books) {
std::cout << entry->name;
}
}
};
Alternatively if you want to stick to a swapping pattern, you should use a variant:
class Library {
public:
std::variant<std::array<Item, 2>, std::array<Item2, 2> > books = std::array<Item, 2>();
void setToItem2() {
books = std::array<Item2, 2>();
}
void printBooks() {
auto *b = std::get_if< std::array<Item, 2> >(&books);
if (nullptr != b) {
for (auto& entry : *b) {
std::cout << entry.name << "\n";
}
}
else {
auto* b2 = std::get_if< std::array<Item2, 2> >(&books);
for (auto& entry : *b2) {
std::cout << entry.name << "\n";
}
}
}
};
int main() {
Library a;
a.printBooks();
a.setToItem2();
a.printBooks();
return 0;
}
I want to create objects with polymorphic types based on a string I read from some config text file. A naive simple solution to this is to assign a string to each possible type and then compare the config string in an else-if chain to all the defined types. Something like:
class Base
{
virtual std::string GetStringType() = 0;
};
class Derived1 : public Base
{
std::string GetStringType() override { return "Derived1"; }
};
class Derived2 : public Base
{
std::string GetStringType() override { return "Derived2"; }
};
// etc ...
void main(int argc, char *argv[])
{
std::unique_ptr<Base> ptr;
auto derived1 = std::make_unique<Derived1>();
auto derived2 = std::make_unique<Derived2>();
// etc ...
std::string stringType(argv[1]);
if (stringType == derived1->GetStringType())
ptr = std::make_unique<decltype(derived1)>();
else if (stringType == derived2->GetStringType())
ptr = std::make_unique<decltype(derived2)>();
// etc ...
}
However, with this approach, each time a new derived class is added, a new else-if branch needs to be manually added, and I am trying to avoid that. Is there a better, more automatic approach to this?
Also, in an ideal scenario, when a new derived class is defined somewhere (just defined, not instantiated), I would like to check against it automatically also. Is this somehow possible? I'd be happy for any solution that works, macros included.
A simple map-based factory can do the trick:
#include <map>
#include <functional>
#include <string>
#include <memory>
class Base
{
public:
virtual ~Base() = default;
static std::unique_ptr<Base> create(const std::string& name) {
return factories_.at(name)();
}
template<typename T>
static void registerDerived() {
static_assert(std::is_base_of_v<Base, T>);
factories_[T::GetStringType()] = std::make_unique<T>;
}
private:
static std::map<std::string, std::function<std::unique_ptr<Base>()>> factories_;
};
std::map<std::string, std::function<std::unique_ptr<Base>()>> Base::factories_;
class Derived1 : public Base
{
public:
static std::string GetStringType() { return "Derived1"; }
};
class Derived2 : public Base
{
public:
static std::string GetStringType() { return "Derived2"; }
};
int main(int argc, char *argv[]) {
Base::registerDerived<Derived1>();
Base::registerDerived<Derived2>();
// etc...
std::unique_ptr<Base> ptr = Base::create(argv[1]);
// ...
}
This is an example based on unordered_map, with an explicit class factory.
Decoupling the creation of the instances completely from the base class.
(Separation of concerns)
#include <type_traits>
#include <string>
#include <functional>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <stdexcept>
#include <sstream>
//-------------------------------------------------------------------------------------------------
class Base
{
public:
virtual void Hello() = 0;
};
class Class1 : public Base
{
public:
void Hello() override
{
std::cout << "Class1\n";
}
};
class Class2 : public Base
{
public:
void Hello() override
{
std::cout << "Class2\n";
}
};
//-------------------------------------------------------------------------------------------------
class ClassFactory final
{
public:
// For each class registered at a function to the map
// that will create a unique_ptr to a new instance of that class
template<typename class_t>
void Register(const std::string& string)
{
static_assert(std::is_base_of_v<Base, class_t>, "You can only register classes derived from Base");
// create_function is a lambda
auto create_function = [] { return std::make_unique<class_t>(); };
// add the function to the map, with the string as key
create_function_map.insert({ string,create_function });
}
std::unique_ptr<Base> Create(const std::string& string)
{
// if nothing is found for the string then throw an exception
if (create_function_map.find(string) == create_function_map.end())
{
std::ostringstream os;
os << "No class registered for string : " << string;
throw std::invalid_argument(os.str());
}
// otherwise call the function that will make an instance of the
// derived class
auto create_function = create_function_map.at(string);
return create_function();
}
static ClassFactory& Instance()
{
static ClassFactory instance;
return instance;
}
private:
ClassFactory() = default;
~ClassFactory() = default;
// use unordered_map it has lookup complexity of O(1)
// std::map has a lookup complexity of O(n log(n))
std::unordered_map<std::string, std::function<std::unique_ptr<Base>()>> create_function_map;
};
//-------------------------------------------------------------------------------------------------
int main()
{
// For each new class register a name, this is the only code
// you need to expand to add new classes
// factory is a singleton so you can reuse it throughout your code
// without having to pass it around.
auto& factory = ClassFactory::Instance();
factory.Register<Class1>("Class1");
factory.Register<Class2>("Class2");
// create an instance of an object of type Class1.
auto object1 = factory.Create("Class1");
object1->Hello();
// create an instance of an object of type Class2.
auto object2 = factory.Create("Class2");
object2->Hello();
return 0;
}
This question already has answers here:
Why doesn't polymorphism work without pointers/references?
(6 answers)
What does slicing mean in C++?
(4 answers)
How to use polymorphism to access derived class vector member from base class?
(1 answer)
Closed 3 years ago.
I have several classes that inherit from one main class. For the sake of simplicity, I have over-simplified the class definitions to make it short and direct to the point.
animal.h
main class which all other classes inherit from:
class Animal {
protected:
string name;
public:
Animal(string name);
virtual string toString() { return "I am an animal"; }
};
bird.h
class Bird: public Animal {
private:
bool canFly;
public:
Bird(string name, bool canFly = true)
: Animal(name) // call the super class constructor with its parameter
{
this->canFly = canFly;
}
string toString() { return "I am a bird"; }
};
indect.h
class Insect: public Animal {
private:
int numberOfLegs;
public:
Insect(string name, int numberOfLegs) : Animal(name) {
this->numberOfLegs = numberOfLegs;
}
string toString() { return "I am an insect."; }
};
Now, I need to declare a vector<Animal> that will hold several instances of each inherited class.
main.cpp
#include <iostream>
#include "animal.h"
#include "bird.h"
#include "insect.h"
// assume that I handled the issue of preventing including a file more than once
// using #ifndef #define and #endif in each header file.
int main() {
vector<Animal> creatures;
creatures.push_back(Bird("duck", true));
creatures.push_back(Bird("penguin", false));
creatures.push_back(Insect("spider", 8));
creatures.push_back(Insect("centipede",44));
// now iterate through the creatures and call their toString()
for(int i=0; i<creatures.size(); i++) {
cout << creatures[i].toString() << endl;
}
}
I expected the following output:
I am a bird
I am a bird
I am an insect
I am an insect
but I got:
I am an animal
I am an animal
I am an animal
I am an animal
I know this has to do with the line 'vector creatures;. It is calling the constructor for Animal. But my intention is to tell the compiler, that this creaturespoints to an array ofAnimalinherited classes, might beBirdmight beinsect, the point is: they all implement their own unique respective version of toString()`.
What can I do to declare a polymorphic array of objects that are inherited from the same ancestor?
You cannot use a value semantic (read about object slicing). You must use pointers.
Example:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Animal
{
protected:
std::string name;
public:
Animal(std::string name) : name(name)
{
}
virtual std::string toString()
{
return "I am an animal";
}
virtual ~Animal()
{
}
};
class Bird : public Animal
{
private:
bool canFly;
public:
Bird(std::string name, bool canFly = true) : Animal(name) // call the super class constructor with its parameter
{
this->canFly = canFly;
}
std::string toString()
{
return "I am a bird";
}
};
class Insect : public Animal
{
private:
int numberOfLegs;
public:
Insect(std::string name, int numberOfLegs) : Animal(name)
{
this->numberOfLegs = numberOfLegs;
}
std::string toString()
{
return "I am an insect.";
}
};
int main()
{
std::vector<std::unique_ptr<Animal>> creatures;
creatures.emplace_back(new Bird("duck", true));
creatures.emplace_back(new Bird("penguin", false));
creatures.emplace_back(new Insect("spider", 8));
creatures.emplace_back(new Insect("centipede", 44));
// now iterate through the creatures and call their toString()
for (int i = 0; i < creatures.size(); i++)
{
std::cout << creatures[i]->toString() << std::endl;
}
}
prints:
I am a bird
I am a bird
I am an insect.
I am an insect.
I also recommend reading about Sean parent Run Time Polymorphism. The idea is as follows:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Animal
{
public:
struct Interface
{
virtual std::string toString() const = 0;
virtual ~Interface() = default;
};
std::shared_ptr<const Interface> _p;
public:
Animal(Interface* p) : _p(p)
{
}
std::string toString() const
{
return _p->toString();
}
};
class Bird : public Animal::Interface
{
private:
std::string _name;
bool _canFly;
public:
Bird(std::string name, bool canFly = true) : _name(name), _canFly(canFly)
{
}
std::string toString() const override
{
return "I am a bird";
}
};
class Insect : public Animal::Interface
{
private:
std::string _name;
int _numberOfLegs;
public:
Insect(std::string name, int numberOfLegs)
: _name(name), _numberOfLegs(numberOfLegs)
{
}
std::string toString() const override
{
return "I am an insect.";
}
};
int main()
{
std::vector<Animal> creatures;
creatures.emplace_back(new Bird("duck", true));
creatures.emplace_back(new Bird("penguin", false));
creatures.emplace_back(new Insect("spider", 8));
creatures.emplace_back(new Insect("centipede", 44));
// now iterate through the creatures and call their toString()
for (int i = 0; i < creatures.size(); i++)
{
std::cout << creatures[i].toString() << std::endl;
}
}
Problem is with creatures.push_back(Bird("duck", true));
You are creating a Bird object and copying that in the Animal object.
One way is to create objects dynamically so that correct function call can resolve using vtable.
Modify this part of your code and it will work fine.
vector<Animal *> creatures;
creatures.push_back(new Bird("duck", true));
creatures.push_back(new Bird("penguin", false));
creatures.push_back(new Insect("spider", 8));
creatures.push_back(new Insect("centipede",44));
Edit: Make sure to release the memory before creatures goes out of scope.
C++ objects are values with specific types. This differs from many languages where variables always hold references to objects, so they can hold references to derived objects just as easily.
If you copy an instance if a derived class onto an obhect of a base class, you get slicing: only the base class data is copied, and the type of the assignee is still that of the base class.
To achieve polymorphic behaviour in C++ you need to either use std::variant to specify the allowed possibilities, in which case the object will hold one of the options, and will switch type between them when assigned to, or you need to use a pointer to the base class, which can hold a pointer to any derived type, but you must then be wary of memory leaks. You do need to use std::visit or std::get to retrieve the values though.
If you are going to use pointers you should always use std::shared_ptr or std::unique_ptr to manage the objects in order to avoid memory leaks.
Code with variant:
int main() {
vector<std::variant<Bird,Insect>> creatures;
creatures.push_back(Bird("duck", true));
creatures.push_back(Bird("penguin", false));
creatures.push_back(Insect("spider", 8));
creatures.push_back(Insect("centipede",44));
// now iterate through the creatures and call their toString()
for(int i=0; i<creatures.size(); i++) {
cout << std::visit([](auto const& creature){
return creature.toString();
},creatures[i]) << endl;
}
}
Code with pointers:
int main()
{
std::vector<std::unique_ptr<Animal>> creatures;
creatures.emplace_back(std::make_unique<Bird>("duck", true));
creatures.emplace_back(std::make_unique<Bird>("penguin", false));
creatures.emplace_back(std::make_unique<Insect>("spider", 8));
creatures.emplace_back(std::make_unique<Insect>("centipede", 44));
// now iterate through the creatures and call their toString()
for (int i = 0; i < creatures.size(); i++)
{
std::cout << creatures[i]->toString() << std::endl;
}
}
Code with std::shared_ptr is equivalent: just replace unique with shared everywhere.
I would like to have to counters for each class type that that was ever instantiated. As a starting point someone sugested this approach as an example:
class Person
{
public:
Person() {
objects.push_back(this);
}
virtual ~Person() {
objects.erase(this);
}
static void print_types()
{
for (auto pers : container)
{
std::cout << typeid(*pers).name() << "\n";
}
}
private:
static std::set<const Person*> objects;
};
class Employee : public Person
{
};
class Employee2 : public Employee
{
};
Each time one of the classes is instatiated I keep track of the objects and I can use print_types() to know how many of which type I've created so far. Notice that Employee2 inherits from Employee and not from Person (i need this to work for chain inheritance)
I would like to extend this so that I have two counters per type: created and alive. The problem is that you can't easily mantain the counters from the constructor/destructor of the base class, Person, because typeid(*this) will return the base class type when called from constructor/destructor.
Another suggestion was to use CRTP pattern but this doesn't work when you use chained inheritance.
Is there another way to implement such counters ?
I just played around a bit. Maybe this helps you. It always prints the value of the right class (not the base class).
But it's practically the same^^.
Header:
#include <set>
#include <string>
class observeable;
class observer
{
public:
observer() = delete;
static void print();
static std::set< observeable* > items;
};
class observeable
{
public:
observeable();
virtual ~observeable();
virtual std::string get_typeid();
};
Source:
std::set< observeable* > observer::items;
void observer::print()
{
std::cout << "Called" << std::endl;
for( auto item : items )
std::cout << item->get_typeid() << std::endl;
}
observeable::observeable()
{
observer::items.insert( this );
}
observeable::~observeable()
{
observer::items.erase( this );
}
std::string observeable::get_typeid()
{
return std::string( typeid(*this).name() );
}
Main:
#include <memory>
class A : observeable
{};
class B : A
{};
class C : observeable
{};
int main()
{
A a;
B b;
observer::print(); // A B present
{
C d;
}
observer::print(); // no C present
auto d_heap = std::shared_ptr<C>( new C() );
observer::print(); // C present
return 0;
}
This question already has answers here:
Observer design pattern in C++
(7 answers)
Closed 9 years ago.
Can Class1 warn Class2 that it has changed without me having to create a method to access the Class1 object in Class2? Basically I want to stick to the dot notation.. No setters and getters.
class Class1 {
public:
void operator=(Class1 class1Obj) {
// call class1ObjWasChanged() which is in Class2 from here
}
};
class Class2 {
public:
Class1 class1Obj;
void class1ObjWasChanged() {
std::cout << "class1Obj was changed!!";
}
};
int main() {
Class2 class2Obj;
Class1 someClass1Obj;
class2Obj.class1Obj = someClass1Obj;
}
the above code should somehow, if possible, call class1ObjWasChanged(). Is that possible being that Class1 doesn't know Class2 exists?
As suggested in the comments you can apply the Observer pattern to accomplish this. One easy way to do this is to provide an Observer class that defines one or more pure virtual functions that will be called when a change is made. Then you will need to maintain a list of objects that want to be notified of changes. After you have put that together you can add the necessary logic in your copy assignment operator to call all objects that have requested change notifications.
#include <vector>
#include <iostream>
#include <string>
class Observable
{
public:
// Provide class that defines the requirements of an observer class
// that is capable of receiving change notifications.
struct Observer
{
virtual ~Observer() {}
// Pure virtual function requires all derived classes to
// provide an implementation of the function.
virtual void onChanged(const Observable& object) = 0;
};
// constructor
Observable(const std::string& name) : name_(name) {}
// Let the outside world add observers that will be notified.
void addObserver(Observer* ob)
{
observers_.push_back(ob);
}
// copy assignment operator
Observable& operator=(const Observable& other)
{
name_ = other.name_;
// alert the observers that want to know about changes
for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
(*it)->onChanged(*this);
}
return *this;
}
std::string name() const
{
return name_;
}
private:
std::string name_;
std::vector<Observer*> observers_;
};
class PeepingTom : public Observable::Observer
{
public:
void onChanged(const Observable& object)
{
std::cout
<< "object name was changed to '"
<< object.name()
<< "'"
<< std::endl;
}
};
int main()
{
PeepingTom observer;
Observable changable("some name");
// Add the observer
changable.addObserver(&observer);
std::cout
<< "`changable` is currently name named '"
<< changable.name()
<< "'"
<< std::endl;
// Change it
changable = Observable("new name");
}