On Thu, 2011-12-15 at 16:07 +1100, Adelle Hartley wrote:
> I have two classes encapsulating resources associated with a database 
> connection: Driver & Connection
> 
> I am writing a one-time process that obtains a Driver instance, opens a 
> Connection, and then does some work via that Connection.
> 
> void DoIt(const char* connection_string)
> {
>    const Driver = get_driver("mysql");
>    Connection con(Driver, connection_string);
> 
>    // ... Do some stuff here.
> }
> 
> I need very much for the Driver instance to outlive the Connection 
> object.

C++ guarantees object destructors are executed in reverse order of
object construction.  So your example is correct code, and the lifetimes
are as required.

>   As it happens, this works as is, but if I introduce a third 
> object to manage "global" setup/teardown:
> 
> void DoIt(const char* connection_string)
> {
>    GlobalStuff globals;
>    const Driver driver("mysql");
>    Connection connection(driver, connection_string);
> 
>    // ... Do some stuff here.
> }

Have you considered using tr1::shared_ptr to manage lifetimes?
When the last reference is destroyed, so is the referenced object.
That is, have your class connection constructor take one of these "smart
pointers", and it remembers the driver instance as a "smart pointer",
too.

class Driver
{
public:
    typedef tr1::shared_ptr<Driver> pointer;

private:
    // By making the constructor private, you ensure that
    // clients of this API must use the create class method,
    // below.  That is, the only way to get an instance is
    // to get a managed instance.
    Driver(...args...);

public:
    // create a managed instance of Driver
    static pointer create(...args...) {
        return pointer(new driver(...args...)); }
};

// if Driver is an abstract base class, then the Driver() constructor is
// protected not private, and instead the derived classes' constructors
// are private.


class Connection
{
public:
    Connection(const Driver::pointer &ptr) : driver(ptr) { }
        // Note: "ptr" is const, not *ptr

    blah blah

private:
    // The compiler will destroy this *after* any clean-up
    // code you put in the ~connection() destructor.
    Driver::pointer driver;
};


void
DoIt(const char *connection_string)
{
    const Driver::pointer driver = Driver::create("mysql");
    Connection connection(driver, connection_string);

    // ... Do some stuff here.
}

A whole bunch of my open source projects use these techniques, see
SRecord (http://srecord.sf.net/) or Tardy (http://tardy.sf.net/) or
Uncia or PlasticFS or ucsd-psystem-xc or ucsd-psystem-fs, or ...


-- 
Peter Miller <pmil...@people.net.au>
_______________________________________________
coders mailing list
coders@slug.org.au
http://lists.slug.org.au/listinfo/coders

Reply via email to