Given a code below
class Base {
public:
virtual void callThisOnce();
};
class Derived_A: public Base {};
class Derived_B: public Base {};
void function(std::vector<Base> v) {
for (auto i : v)
i.callThisOnce();
}
Vector v
contains either or both Derived_A
and Derived_B
objects.
What I want to archive is that when ever function
is called, if in v
there is a object belong to class Derived_A
, Derived_A::callThisOnce
should be executed once; and if there is a object belong to class Derived_B
, Derived_B::callThisOnce
should be executed once.
I need a coding pattern that make it easiest to create Derived_C
.
I tried this
class Derived_A: public Base {
virtual void callThisOnce(){
if(!_mutex.try_lock())
return;
/* Do something */
}
static std::mutex _mutex;
};
void function(std::vector<Base> v) {
for (auto i : v)
i.callThisOnce();
Derived_A::_mutex.try_lock(); // this call prevent undefined behavior of mutex::unlock
Derived_A::_mutex.unlock();
}
This pattern make me create a static mutex an required me to call std::mutex::unlock
of all class. But it's seem to leave many problems.
Is there a better method to archive the same?
CodePudding user response:
std::vector<Base> v
vector v contains either or both Derived_A and Derived_B objects.
What you describe isn't possible. A vector of Base
can only contain objects of type Base
and not objects of type Derived_A
or Derived_B
.
If you want polymorphic storage, then you need indirection. And if you want to combine indirection with the lifetime of the vector, then you need smart pointers. And if you want unique ownership, then you need a virtual destructor:
struct Base {
virtual void callThisOnce();
virtual ~Base() = default;
};
struct Derived_A: Base {};
struct Derived_B: Base {};
void function(std::vector<std::unique_ptr<Base>>& v);
To call a member function once per unique dynamic type, you could store each encountered std::type_index
in a set and call the function only for the first one:
std::unordered_set<std::type_index> types;
for (auto&& ptr : v) {
auto [it, first] = types.emplace(typeid(*ptr));
if (first) {
ptr->callThisOnce();
}
CodePudding user response:
If I got your intention right one way to achieve this is by combining runtime polymorphism (the correct function gets called) with using a static variable in each class (to track down if function is called only once per class and not per object):
class Base {
public:
virtual void callThisOnce() {
if (!base_called) {
std::cout << "Base::callThisOnce" << std::endl;
base_called = true;
}
};
static bool base_called;
};
class Derived_A: public Base {
public:
virtual void callThisOnce() override {
if (!deriveda_called) {
std::cout << "Derived_A::callThisOnce" << std::endl;
deriveda_called = true;
}
}
static bool deriveda_called;
};
class Derived_B: public Base {
public:
virtual void callThisOnce() override {
if (!derivedb_called) {
std::cout << "Derived_B::callThisOnce" << std::endl;
derivedb_called = true;
}
}
static bool derivedb_called;
};
bool Base::base_called;
bool Derived_A::deriveda_called;
bool Derived_B::derivedb_called;
void function(const std::vector<Base*> &v) {
Base::base_called = false;
Derived_A::deriveda_called = false;
Derived_B::derivedb_called = false;
for (auto i : v)
i->callThisOnce();
}
int main() {
std::vector<Base*> v;
Base b1, b2;
Derived_A da1, da2;
Derived_B db1, db2;
v.push_back(&b1);
v.push_back(&b2);
v.push_back(&da1);
v.push_back(&da2);
v.push_back(&db1);
v.push_back(&db2);
function(v);
return 0;
}
which will output:
Base::callThisOnce
Derived_A::callThisOnce
Derived_B::callThisOnce
EDIT:
The static
variables should get reset after the callThisOnce
calls to avoid some unwanted behaviour with recursive calls of function
(thanks to @Jarod42)
void function(const std::vector<Base*> &v) {
for (auto i : v)
i->callThisOnce();
Base::base_called = false;
Derived_A::deriveda_called = false;
Derived_B::derivedb_called = false;
}