I'm working on a class that handles interaction with a remote process that may or may not be available; indeed in most cases it won't be. If it's not, an object of that class has no purpose in life and needs to go away.
Is it less ugly to:
connect()
method, returning an error code if the process isn't there.In option 1), the calling code will of course have to wrap its instantiation of that class and everything else that deals with it in a try()
block. In option 2, it can simply check the return value from connect(), and return (destroying the object) if it failed, but it's less RAII-compliant,
Relatedly, if I go with option 1), is it better to throw one of the std::exception classes, derive my own exception class therefrom, roll my own underived exception class, or just throw a string? I'd like to include some indication of the failure, which seems to rule out the first of these.
Edited to clarify: The remote process is on the same machine, so it's pretty unlikely that the ::connect()
call will block.
The most common mistake to do in a constructor as well as in a destructor, is to use polymorphism. Polymorphism often does not work in constructors !
The purpose of constructor is to initialize the object of a class while the purpose of a method is to perform a task by executing java code. Constructors cannot be abstract, final, static and synchronised while methods can be. Constructors do not have return types while methods do.
C++ Constructors. Constructors are methods that are automatically executed every time you create an object. The purpose of a constructor is to construct an object and assign values to the object's members. A constructor takes the same name as the class to which it belongs, and does not return any values.
A constructor creates an Object of the class that it is in by initializing all the instance variables and creating a place in memory to hold the Object. For example you may want to initialize an integer variable to 10 or 20 based on a certain condition, when your class is created.
If your connection object is effectively non-functional if the connection fails then it doesn't make sense to have the object exist if all its other methods will always do nothing or throw exceptions. For this reason I would perform the connect in a constructor and fail by throwing an exception (derived from std::exception
) if this method fails.
However, you are right that clients of the class may need to be aware that the constructor might block or fail. For this reason I might choose to make the constructor private and use a static factory method (named constructor idiom) so that clients have to make an explicit MakeConnection
call.
It is still the client's responsibility to determine if not having a connection is fatal to it, or whether it can handle an offline mode. In the former case it can own a connection by value and let any connection failure propogate to its clients; in the latter it can own the object via a pointer, preferably 'smart'. In the latter case it might choose to attempt construction of the owned connection in its constructor or it might defer it until needed.
E.g. (warning: code all completely untested)
class Connection
{
Connection(); // Actually make the connection, may throw
// ...
public:
static Connection MakeConnection() { return Connection(); }
// ...
};
Here's a class that requires a working connection.
class MustHaveConnection
{
public:
// You can't create a MustHaveConnection if `MakeConnection` fails
MustHaveConnection()
: _connection(Connection::MakeConnection())
{
}
private:
Connection _connection;
};
Here's a class that can work without one.
class OptionalConnection
{
public:
// You can create a OptionalConnectionif `MakeConnection` fails
// 'offline' mode can be determined by whether _connection is NULL
OptionalConnection()
{
try
{
_connection.reset(new Connection(Connection::MakeConnection()));
}
catch (const std::exception&)
{
// Failure *is* an option, it would be better to capture a more
// specific exception if possible.
}
}
OptionalConnection(const OptionalConnection&);
OptionalConnection& operator=(const OptionalConnection&);
private:
std::auto_ptr<Connection> _connection;
}
And finally one that creates one on demand, and propogates exceptions to the caller.
class OnDemandConnection
{
public:
OnDemandConnection()
{
}
OnDemandConnection(const OnDemandConnection&);
OnDemandConnection& operator=(const OnDemandConnection&);
// Propgates exceptions to caller
void UseConnection()
{
if (_connection.get() == NULL)
_connection.reset(new Connection(Connection::MakeConnection()));
// do something with _connection
}
private:
std::auto_ptr<Connection> _connection;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With