Reputation: 867
Let's say I have pure abstract class IHandler
and my class that derives from it:
class IHandler
{
public:
virtual int process_input(char input) = 0;
};
class MyEngine : protected IHandler
{
public:
virtual int process_input(char input) { /* implementation */ }
};
I want to inherit that class in my MyEngine so that I can pass MyEngine*
to anyone expecting IHandler*
and for them to be able to use process_input
.
However I don't want to allow access through MyEngine*
as I don't want to expose implementation details.
MyEngine* ptr = new MyEngine();
ptr->process_input('a'); //NOT POSSIBLE
static_cast<IHandler*>(ptr)->process_input('a'); //OK
IHandler* ptr2 = ptr; //OK
ptr2->process_input('a'); //OK
Can this be done via protected inheritance and implicit casting? I only managed to get:
conversion from 'MyEngine *' to 'IHandler *' exists, but is inaccessible
Since I come from C# background, this is basically explicit interface implementation in C#. Is this a valid approach in C++?
Additional:
To give a better idea why I want to do this, consider following:
Class TcpConnection
implements communication over TCP, and in its constructor expects pointer to interface ITcpEventHandler
.
When TcpConnection
gets some data on a socket, it passes that data to its ITcpEventHandler
using ITcpEventHandler::incomingData
, or when it polls for outgoing data it uses ITcpEventHandler::getOutgoingData
.
My class HttpClient
uses TcpConnection
(aggregation) and passes itself to TcpConnection
constructor, and does processing in those interface methods.
So TcpConnection
has to implement those methods, but I don't want users using HttpClient
to have direct access to ITcpEventHandler
methods (incomingData
, getOutgoingData
). They should not be able to call incomingData
or getOutgoingData
directly.
Hope this clarifies my use case.
Upvotes: 2
Views: 3404
Reputation: 96301
It's slightly hard to understand the real goal you hope to achieve here, because whether you call the method on the parent or child, as long as it's virtual the same one will be called.
That said you have a couple options.
You could make it so the user can't get a pointer (or object) of the child type by forcing a create call that returns an interface. Then you don't have to worry about artificial restrictions, they just can't get a child at all:
class Concrete : public Interface
{
public:
static Interface* create() { return new Concrete; }
private:
Concrete() { }
};
You could override the interface as protected
as shown in a different answer.
You could utilize the non-virtual interface pattern to make the entire accessible public interface defined in the parent. Then it doesn't matter what object they have, they always get the public API from the interface class:
class Interface
{
public:
void foo() { foo_impl(); }
private:
virtual void foo_impl() = 0;
};
class Concrete
{
private:
virtual void foo_impl() { }
};
Upvotes: 2
Reputation: 126542
Deriving with protected
makes the members of the base class inaccessible through a pointer to the derived class, and disallows the implicit conversion.
It seems to me that what you want is not to forbid access through the base class (interface), but rather through the derived class (concrete implementation):
class IHandler
{
public:
virtual int process_input(char input) = 0; //pure virtual
virtual std::string name() { return "IHandler"; } //simple implementation
};
class MyEngine : public IHandler
// ^^^^^^
{
protected: // <== Make the functions inaccessible from a pointer
// or reference to `MyEngine`.
virtual int process_input(char input) { return 0; } //override pure virtual
using IHandler::name; //use IHandler version
};
Here, in the derived class you basically override the visibility of the process_input
function, so that clients can only call them through a pointer or reference to the base class.
This way you will make this impossible:
MyEngine* ptr = new MyEngine();
ptr->process_input('a'); // ERROR!
std::cout << ptr->name(); // ERROR!
But this will be possible:
IHandler* ptr = new MyEngine();
ptr->process_input('a'); // OK
std::cout << ptr->name(); // OK
Upvotes: 6
Reputation: 13217
In C++ protected
and private
inheritance serve the use of inheritance of the implementation. This is, you define a class with methods, a template class for example and when you want to use its functionality but not its interface, you inherit protected
or private
. So actually your base class would need to define the methods you want to use in the sub-class.
Here is a link on this topic. It really is difficult, I agree.
Upvotes: 1