• Tidak ada hasil yang ditemukan

Provide access to raw resources in resource- managing classes

ptg7544714 to a raw resource and that when the RAII object is copied, owner-

ship of the resource is transferred from the copied object to the copying object. As explained in Item 13, this is the meaning of

“copy” used by auto_ptr.

The copying functions (copy constructor and copy assignment opera- tor) may be generated by compilers, so unless the compiler-generated versions will do what you want (Item 5 explains the default behavior), you’ll need to write them yourself. In some cases, you’ll also want to support generalized versions of these functions. Such versions are described in Item 45.

Things to Remember

Copying an RAII object entails copying the resource it manages, so the copying behavior of the resource determines the copying behav- ior of the RAII object.

Common RAII class copying behaviors are disallowing copying and performing reference counting, but other behaviors are possible.

Item 15: Provide access to raw resources in resource-

ptg7544714 You’d like to call it like this,

int days = daysHeld(pInv); // error!

but the code won’t compile: daysHeld wants a raw Investment* pointer, but you’re passing an object of type tr1::shared_ptr<Investment>. You need a way to convert an object of the RAII class (in this case, tr1::shared_ptr) into the raw resource it contains (e.g., the underlying Investment*). There are two general ways to do it: explicit conversion and implicit conversion.

tr1::shared_ptr and auto_ptr both offer a get member function to per- form an explicit conversion, i.e., to return (a copy of) the raw pointer inside the smart pointer object:

int days = daysHeld(pInv.get()); // fine, passes the raw pointer // in pInv to daysHeld

Like virtually all smart pointer classes, tr1::shared_ptr and auto_ptr also overload the pointer dereferencing operators (operator-> and operator*), and this allows implicit conversion to the underlying raw pointers:

class Investment { // root class for a hierarchy

public: // of investment types

bool isTaxFree() const;

...

};

Investment* createInvestment(); // factory function std::tr1::shared_ptr<Investment> // have tr1::shared_ptr

pi1(createInvestment()); // manage a resource

bool taxable1 = !(pi1->isTaxFree()); // access resource // via operator->

...

std::auto_ptr<Investment> pi2(createInvestment()); // have auto_ptr // manage a // resource bool taxable2 = !((*pi2).isTaxFree()); // access resource

// via operator*

...

Because it is sometimes necessary to get at the raw resource inside an RAII object, some RAII class designers grease the skids by offering an implicit conversion function. For example, consider this RAII class for fonts that are native to a C API:

FontHandle getFont(); // from C API — params omitted // for simplicity

void releaseFont(FontHandle fh); // from the same C API

ptg7544714

class Font { // RAII class

public:

explicit Font(FontHandle fh) // acquire resource;

: f(fh) // use pass-by-value, because the

{} // C API does

~Font() { releaseFont(f ); } // release resource

... // handle copying (see Item 14)

private:

FontHandle f; // the raw font resource

};

Assuming there’s a large font-related C API that deals entirely with FontHandles, there will be a frequent need to convert from Font objects to FontHandles. The Font class could offer an explicit conversion func- tion such as get:

class Font { public:

...

FontHandle get() const { return f; } // explicit conversion function ...

};

Unfortunately, this would require that clients call get every time they want to communicate with the API:

void changeFontSize(FontHandle f, int newSize); // from the C API Font f(getFont());

int newFontSize;

...

changeFontSize(f.get(), newFontSize); // explicitly convert // Font to FontHandle Some programmers might find the need to explicitly request such con- versions off-putting enough to avoid using the class. That, in turn, would increase the chances of leaking fonts, the very thing the Font class is designed to prevent.

The alternative is to have Font offer an implicit conversion function to its FontHandle:

class Font { public:

...

operator FontHandle() const // implicit conversion function { return f; }

...

};

That makes calling into the C API easy and natural:

ptg7544714 Font f(getFont());

int newFontSize;

...

changeFontSize(f, newFontSize); // implicitly convert Font // to FontHandle

The downside is that implicit conversions increase the chance of errors. For example, a client might accidently create a FontHandle when a Font was intended:

Font f1(getFont());

...

FontHandle f2 = f1; // oops! meant to copy a Font // object, but instead implicitly // converted f1 into its underlying // FontHandle, then copied that Now the program has a FontHandle being managed by the Font object f1, but the FontHandle is also available for direct use as f2. That’s almost never good. For example, when f1 is destroyed, the font will be released, and f2 will dangle.

The decision about whether to offer explicit conversion from an RAII class to its underlying resource (e.g., via a get member function) or whether to allow implicit conversion is one that depends on the spe- cific task the RAII class is designed to perform and the circumstances in which it is intended to be used. The best design is likely to be the one that adheres to Item 18’s advice to make interfaces easy to use correctly and hard to use incorrectly. Often, an explicit conversion function like get is the preferable path, because it minimizes the chances of unintended type conversions. Sometimes, however, the naturalness of use arising from implicit type conversions will tip the scales in that direction.

It may have occurred to you that functions returning the raw resource inside an RAII class are contrary to encapsulation. That’s true, but it’s not the design disaster it may at first appear. RAII classes don’t exist to encapsulate something; they exist to ensure that a particular action

— resource release — takes place. If desired, encapsulation of the resource can be layered on top of this primary functionality, but it’s not necessary. Furthermore, some RAII classes combine true encap- sulation of implementation with very loose encapsulation of the underlying resource. For example, tr1::shared_ptr encapsulates all its reference-counting machinery, but it still offers easy access to the raw pointer it contains. Like most well-designed classes, it hides what cli-

ptg7544714 ents don’t need to see, but it makes available those things that clients

honestly need to access.

Things to Remember

APIs often require access to raw resources, so each RAII class should offer a way to get at the resource it manages.

Access may be via explicit conversion or implicit conversion. In gen- eral, explicit conversion is safer, but implicit conversion is more con- venient for clients.

Item 16: Use the same form in corresponding uses of

Dokumen terkait