Reputation: 10273
I am deriving an object from two parent classes. These two parents each have different types of properties, but I want the child to keep them in sync with each other. However, I want to disallow users of the library from treating Child
like a ParentA
or a ParentB
accidentally via slicing. For example:
#include <iostream>
class ParentA
{
public:
void modify()
{
std::cout << "modifyA" << std::endl;
}
void readA()
{
std::cout << "readA" << std::endl;
}
};
class ParentB
{
public:
void modify()
{
std::cout << "modifyB" << std::endl;
}
void readB()
{
std::cout << "readB" << std::endl;
}
};
class Child : public ParentA, public ParentB
{
public:
void modify()
{
// Do some bounds checking to make sure ParentA and ParentB stay in sync, then:
ParentA::modify();
ParentB::modify();
std::cout << "modifyChild" << std::endl;
}
};
void Change(ParentA object)
{
object.modify();
}
int main()
{
std::cout << "This is standard:" << std::endl;
ParentA parentA;
parentA.modify();
ParentB parentB;
parentB.modify();
Child child;
child.readA();
child.readB();
child.modify();
std::cout << "Want to avoid this:" << std::endl;
Change(child);
return 0;
}
This call to Change(child);
calls ParentA
's modify()
function, in which the ParentA
properties can get out of sync with the ParentB
properties, leaving the Child
in a bad state.
There are many functions (the read*()
ones here) in ParentA
and ParentB
that I don't want to have to manually forward from Child
, so I can't derive privately.
Is there a way to make this call to Change(child)
produce a compiler error (without changing the signature of Change
)?
Upvotes: 1
Views: 63
Reputation: 5690
As the comments already say the cleanest way might be to just inherit from ParentA
and ParentB
with private
and forward the needed functions.
I had another idea: You could extract the functionality of ParentA
and ParentB
into 2 abstract classes (AbstractParentA
,AbstractParentB
) and use these classes as base classes.
This would give you the desired behaviour:
#include <iostream>
class AbstractParentA
{
virtual void no_instance() = 0;
public:
void modify()
{
std::cout << "modifyA" << std::endl;
}
void readA()
{
std::cout << "readA" << std::endl;
}
};
class AbstractParentB
{
virtual void no_instance() = 0;
public:
void modify()
{
std::cout << "modifyB" << std::endl;
}
void readB()
{
std::cout << "readB" << std::endl;
}
};
class ParentA : public AbstractParentA
{
virtual void no_instance() override {}
};
class ParentB : public AbstractParentB
{
virtual void no_instance() override {}
};
class Child : public AbstractParentA, public AbstractParentB
{
virtual void no_instance() override {}
public:
void modify()
{
// Do some bounds checking to make sure ParentA and ParentB stay in sync, then:
AbstractParentA::modify();
AbstractParentB::modify();
std::cout << "modifyChild" << std::endl;
}
};
void Change(ParentA object)
{
object.modify();
}
int main()
{
std::cout << "This is standard:" << std::endl;
ParentA parentA;
parentA.modify();
ParentB parentB;
parentB.modify();
Child child;
child.readA();
child.readB();
child.modify();
std::cout << "Want to avoid this:" << std::endl;
Change(child);
return 0;
}
error C2664: 'void Change(ParentA)': cannot convert argument 1 from 'Child'
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Upvotes: 1
Reputation: 96311
There is in fact a way to do this (although said you didn't like it): private
or protected
inheritance is the C++ mechanism to achieve what you want.
Bear in mind that since your child class is trying to keep some sort of invariant between A and B, if you inherit public
ly, someone will find a way to use A or B's interface to violate the invariant anyway so you need to protect against those being used in the child directly, which the restricted inheritance does perfectly.
If there are then some methods in the parent that don't affect the two-class invariant you can using
those down into the public section of Child.
Upvotes: 2