FAQ 30.12 Does auto_ptr enforce the Law of the Big Three and solve the problems associated with remote ownership?

FAQ 30.12 Does auto_ptr enforce the Law of the Big Three and solve the problems associated with remote ownership?

graphics/new_icon.gif

No. auto_ptr<T> plugs leaks, but it doesn't enforce the Law of the Big Three.

When a class uses a plain T* to implement remote ownership, forgetting any of the Big Three causes the compiler to silently generate wrong code. The result is often a disaster at runtime.

Unfortunately, replacing the T* with a managed pointer such as auto_ptr<T> does not correct the problem. The root of the problem is that when an auto_ptr<T> is copied, ownership of the referent is transferred to the copy and the original object's auto_ptr<T> becomes NULL. This is often undesirable. What is needed instead is for the referent to be copied or for a compile-time error to be generated that flags the problem.

The safest solution is to define and use a strict auto_ptr<T>. For example, the following could go into file strict_auto_ptr.h and could be reused whenever anyone wanted a strict auto_ptr<T>. Note that the copy constructor and assignment operator are private: and are undefined, thus making it impossible to copy a strict_auto_ptr<T> object.

 #include <memory> using namespace std; template<class T> class strict_auto_ptr : public auto_ptr<T> { public:   strict_auto_ptr(T* p=NULL) throw() : auto_ptr<T>(p) { } private:   strict_auto_ptr(const strict_auto_ptr&) throw();   void operator= (const strict_auto_ptr&) throw(); }; 

When strict_auto_ptr<T> is used, the compiler either synthesizes the Big Three correctly or causes specific, compile-time errors; it does not allow run-time disasters.

The following example shows a class that implements remote ownership by the managed pointer strict_auto_ptr<Noisy> rather than the plain pointer Noisy*.

 #include <iostream> using namespace std; class Noisy { public:   Noisy() throw();  ~Noisy() throw();   Noisy& operator= (const Noisy&) throw();   Noisy            (const Noisy&) throw(); }; Noisy::Noisy() throw()   { cout << "Noisy::Noisy()\n"; } Noisy::~Noisy() throw()   { cout << "Noisy::~Noisy()\n"; } typedef strict_auto_ptr<Noisy> NoisyPtr; class Fred { public:   Fred() throw(bad_alloc);   //No destructor needed: The Noisy will automagically get   //deleted. The compiler won't synthesize a copy ctor or   //assignment operator, since the strict_auto_ptr version of these   //are private. protected:   NoisyPtr ptr_;  // Like Noisy* ptr_ but better }; Fred::Fred() throw(bad_alloc) : ptr_(new Noisy()) { } void sample() {   Fred x;  //OK: Allocates a new Noisy }          //OK: x is destructed, so its Noisy is deleted int main() { sample(); } 

Because strict_auto_ptr<Noisy>'s destructor deletes the referent, Fred doesn't need an explicit destructor. The Fred::~Fred() synthesized by the compiler is correct.

Because strict_auto_ptr<Noisy>'s copy constructor and assignment operator are private:, the compiler is prevented from synthesizing either the copy constructor or the assignment operator for class Fred. Copying or assigning a Fred produces a specific, compile-time error message. Compare this to using a Noisy*, in which case the compiler silently synthesizes the wrong code, producing disastrous results.

For example, when the GENERATE_ERROR symbol is #defined in the following function, the compiler gives an error message rather than silently doing the wrong thing.

 void disasterAverted(const Fred& x) throw() {   #ifdef GENERATE_ERROR     Fred y = x;  //gives a compile-time error message     y = x;       //gives a compile-time error message   #endif } 

strict_auto_ptr<T> effectively automates the proper delete and prevents the compiler from synthesizing improper copy operations. It plugs leaks and enforces the Law of the Big Three.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net