This is supposed to be very basic.
Layout:
class handler {
public:
handler(Connection *conn) { connection = conn; }
virtual void handle() = 0;
};
class http_status : public handler {
public:
http_status(Connection *conn) : handler(conn) { }
void handle();
};
class http_photoserver : public handler {
public:
http_photoserver(Connection *conn) : handler(conn) { }
void handle();
};
Code:
void pick_and_handle() {
if (connection->http_header.uri_str != "/") {
http_photoserver handler(connection);
} else {
http_status handler(connection);
}
handler.handle();
}
This gives an error:
../handler.cpp:51:10: error: expected unqualified-id before ‘.’ token
I'm guessing because compiler doesn't know what handler is cause object is created inside an if statement. I need to pick a handler based on a condition, how do I do that?
Obviously this code works:
if (connection->http_header.uri_str != "/") {
http_photoserver handler(connection);
handler.handle();
} else {
http_status handler(connection);
handler.handle();
}
But doesn't look very sexy! Is it really the only way in c++?
Use a pointer so you get polymorphic behavior:
auto_ptr<handler> theHandler = (connection->http_header.uri_str != "/") ?
new http_photoserver(connection) :
new http_status(connection);
theHandler->handle();
Of course it's not the only way. But you may have to use pointers:
void pick_and_handle() {
unique_ptr<handler> http_handler;
if (connection->http_header.uri_str != "/")
http_handler.reset(new http_photoserver(connection));
else
http_handler.reset(new http_status(connection));
http_handler->handle();
}
(Instead of unique_ptr
, you can use boost::scoped_ptr
, shared_ptr
, and auto_ptr
also. But in this case, unique_ptr
and boost::scoped_ptr
are most appropriate.)
C++ can only do polymorphism in pointers and references. Note that with your code, the actual type of handler
is not known till runtime. The only thing known is that it will be of one of the subtypes of handler
, so you have to declare a pointer to use the polymorphism:
void pick_and_handle() {
std::auto_ptr<handler> h;
if (connection->http_header.uri_str != "/") {
h.reset(new http_photoserver(connection));
} else {
h.reset(new http_status(connection));
}
h->handle();
}
I use std::auto_ptr
to assure the pointer will be automatically deleted when the function ends.
The object handler
doesn't exist outside the scope in which its defined.
One solution could be runtime polymorphism, that is, define a base class and a virtual function in it, as:
struct base_handler
{
virtual void handle(Connection *conn) = 0; //interface
virtual ~base_handler() {} //must make it virtual!
};
struct http_photoserver : base_handler
{
virtual void handle(Connection *conn) {} //implementation
};
struct http_status : base_handler
{
virtual void handle(Connection *conn) {} //implementation
};
Then use it as:
base_handler *phander ;
if (connection->http_header.uri_str != "/") {
phandler = new http_photoserver(connection);
} else {
phandler = new http_status (connection);
}
phandler->handle();
//...
delete phandler;
Declare a pointer somewhere above that code, and then assign an object later in the if statement. Since they are inherited from the same class, OO teaches us that a child can replace a parent :) .
After that it should work.
Just don't forget to destruct! :)
Hope I helped.
If you go with the pointer approach like the others suggest you should also add a virtual destructor the base class.
This approach can be better expressed using a factory method. Just add a static function in your base class that accepts a connection
and returns a (smart) pointer to a handler
. Put the "pick" logic there.
If you don't want the pointer approach then the second version you posted is the one to use.