S
Steven T. Hatton
I have a couple questions about the design pattern presented in the example
quoted below. I can appreciate why the destructor is protected, but why is
it not virtual? I am forced to assume that I am not expected to derive
from X. But why impose such a restriction?
Also, what are the tradeoffs between declaring createX() to be namespace
local vs. being a static member function of X?
<quote url="http://www.boost.org/libs/smart_ptr/sp_techniques.html">
Using abstract classes for implementation hiding
Another widely used C++ idiom for separating inteface and implementation is
to use abstract base classes and factory functions. The abstract classes
are sometimes called "interfaces" and the pattern is known
as "interface-based programming". Again, shared_ptr can be used as the
return type of the factory functions:
// X.hpp:
class X
{
public:
virtual void f() = 0;
virtual void g() = 0;
protected:
~X() {}
};
shared_ptr<X> createX();
-- X.cpp:
class X_impl: public X
{
private:
X_impl(X_impl const &);
X_impl & operator=(X_impl const &);
public:
virtual void f()
{
// ...
}
virtual void g()
{
// ...
}
};
shared_ptr<X> createX()
{
shared_ptr<X> px(new X_impl);
return px;
}
A key property of shared_ptr is that the allocation, construction,
deallocation, and destruction details are captured at the point of
construction, inside the factory function. Note the protected and
nonvirtual destructor in the example above. The client code cannot, and
does not need to, delete a pointer to X; the shared_ptr<X> instance
returned from createX will correctly call ~X_impl.
</quote>
quoted below. I can appreciate why the destructor is protected, but why is
it not virtual? I am forced to assume that I am not expected to derive
from X. But why impose such a restriction?
Also, what are the tradeoffs between declaring createX() to be namespace
local vs. being a static member function of X?
<quote url="http://www.boost.org/libs/smart_ptr/sp_techniques.html">
Using abstract classes for implementation hiding
Another widely used C++ idiom for separating inteface and implementation is
to use abstract base classes and factory functions. The abstract classes
are sometimes called "interfaces" and the pattern is known
as "interface-based programming". Again, shared_ptr can be used as the
return type of the factory functions:
// X.hpp:
class X
{
public:
virtual void f() = 0;
virtual void g() = 0;
protected:
~X() {}
};
shared_ptr<X> createX();
-- X.cpp:
class X_impl: public X
{
private:
X_impl(X_impl const &);
X_impl & operator=(X_impl const &);
public:
virtual void f()
{
// ...
}
virtual void g()
{
// ...
}
};
shared_ptr<X> createX()
{
shared_ptr<X> px(new X_impl);
return px;
}
A key property of shared_ptr is that the allocation, construction,
deallocation, and destruction details are captured at the point of
construction, inside the factory function. Note the protected and
nonvirtual destructor in the example above. The client code cannot, and
does not need to, delete a pointer to X; the shared_ptr<X> instance
returned from createX will correctly call ~X_impl.
</quote>