To do so, the weak_ptr must be converted to a shared_ptr, because the weak_ptr alone does not allow direct access to the resource.. There are two ways of creating a shared_ptr from a wea
Trang 1weak_ptr_unary(std::equal_to<string>(),string("of using")));
if (it!=vec.end()) {
shared_ptr<string> sp(*++it);
std::cout << *sp << '\n';
}
}
In the example, a vector containing weak_ptrs is created The most interesting line
of code (yes, it's quite a long one) is where we create a weak_ptr_unary_t for use with the find_if algorithm
vector<weak_ptr<string> >::iterator it=std::find_if(
vec.begin(),
vec.end(),
weak_ptr_unary(
std::equal_to<string>(),string("of using")));
The function object is created by passing another function object, std::equal_to, to the helper function weak_ptr_unary, together with the string that is to be used for the matching Because of the fact that weak_ptr_unary_t is compatible with
adaptors (it is compatible because it inherits from std::unary_function), we could compose any type of function object out of it For instance, we could have
searched for the first string not matching "of using":
vector<weak_ptr<string> >::iterator it=std::find_if(
vec.begin(),
vec.end(),
std::not1(
weak_ptr_unary(
std::equal_to<string>(),string("of using"))));
The Boost smart pointers were specifically designed to work well with the
Standard Library That makes it easy for us to create useful components that help
us simplify the usage of these powerful smart pointers Utilities such as
weak_ptr_unary aren't needed all that often; there are libraries that provide general binders that do a much better job of that than weak_ptr_unary.[15] These, too, are typically aware of smart pointer semantics, which makes using them completely transparent to use
Two Idiomatic Ways of Creating a shared_ptr from a weak_ptr
Trang 2As you have seen, when you have a weak_ptr that's observing some resource, you'll eventually want to access that resource To do so, the weak_ptr must be converted to a shared_ptr, because the weak_ptr alone does not allow direct access
to the resource There are two ways of creating a shared_ptr from a weak_ptr: Either pass the weak_ptr to the constructor of shared_ptr or call the weak_ptr
member function lock, which returns a shared_ptr Which to choose depends on whether you consider an empty weak_ptr to be an error or not The shared_ptr constructor accepting a weak_ptr argument will throw an exception of type
bad_weak_ptr if the weak_ptr is empty It should therefore be used only if an
empty weak_ptr constitutes an error When using the weak_ptr function lock, the returned shared_ptr will be empty if the weak_ptr is empty This is the right thing
to do if you need to test for a valid resourcethat is, an empty weak_ptr is expected behavior Furthermore, when using lock, the idiomatic way to use the resource is to initialize it and test it simultaneously, like so:
#include <iostream>
#include <string>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
int main() {
boost::shared_ptr<std::string>
sp(new std::string("Some resource"));
boost::weak_ptr<std::string> wp(sp);
//
if (boost::shared_ptr<std::string> p=wp.lock())
std::cout << "Got it: " << *p << '\n';
else
std::cout << "Nah, the shared_ptr is empty\n";
}
As you can see, the shared_ptr p is initialized with the result of locking the
weak_ptr wp Then p is tested, and only if it is non-empty is the resource accessed
As the shared_ptr is only valid in that scope, there is no chance of inadvertently trying to use it outside of the scope where it is valid The other scenario is when the weak_ptr logically must be non-empty In that case, testing for an empty
shared_ptr is easy to forgetand because the shared_ptr constructor throws an
exception when handed an empty weak_ptr, this is the way to go
#include <iostream>
Trang 3#include <string>
#include "boost/shared_ptr.hpp"
#include "boost/weak_ptr.hpp"
void access_the_resource(boost::weak_ptr<std::string> wp) {
boost::shared_ptr<std::string> sp(wp);
std::cout << *sp << '\n';
}
int main() {
boost::shared_ptr<std::string>
sp(new std::string("Some resource"));
boost::weak_ptr<std::string> wp(sp);
//
access_the_resource(wp);
}
In this example, the function access_the_resource constructs the shared_ptr sp from a weak_ptr It doesn't need to test whether the shared_ptr is empty or not, because if the weak_ptr is empty, an exception of type bad_weak_ptr is thrown, and therefore the function leaves scope immediately; catching and handling the error will be handled where it's suitable This is much better than explicitly testing for an empty shared_ptr and then returning These are the two ways in which to get
a shared_ptr out of a weak_ptr
Summary
weak_ptr is the last piece that we must place on the Boost smart pointer puzzle The weak_ptr abstraction is a very important companion to that of shared_ptr It allows us to break cyclic dependencies It also handles a very common problemthat
of the dangling pointer When sharing a resource, it is common that some of the users of that resource must not take part in its lifetime management This cannot be handled using raw pointers, because when the last shared_ptr is destroyed, it takes the shared resource with it Had raw pointers been used to refer to that resource, there would be no way of knowing whether the resource still exists If it doesn't, accessing it wreaks havoc With weak_ptrs, the information that the shared
resource has been destroyed is propagated to all weak_ptrs observing it, which means that no one can inadvertently access an invalid pointer It's like a special case of the Observer pattern; when the resource is destroyed, those who have
expressed interest in knowing about it are informed
Trang 4Use weak_ptr to
Break cyclic dependencies
Use a shared resource without sharing ownership
Avoid dangling pointers
Smart_ptr Summary
This chapter has introduced the Boost smart pointers, a contribution to the C++ community that can hardly be overestimated For a smart pointer library to be successful, it must take into consideration and correctly handle a great number of factors I'm sure you have seen quite a number of smart pointers, and you might have even been involved in their creation, so you are aware of the effort involved
to get things right Not many smart pointers are as smart as they should be, and that makes the value of a proven library such as Boost.Smart_ptr immense
Being such a central component of software engineering, the smart pointers in Boost have obviously received a lot of attention and thorough review It is
therefore hard to give credit to all who deserve it Many have contributed valuable opinions and have been part of shaping the current smart pointer library However,
a few exceptional people and efforts must be mentioned here:
Greg Colvin, the father of auto_ptr, also suggested counted_ptr, which later became what we now call shared_ptr
Beman Dawes revived the discussion about smart pointers and proposed that the original semantics as suggested by Greg Colvin be considered
Peter Dimov redesigned the smart pointer classes, adding thread safety, intrusive_ptr, and weak_ptr
It is intriguing that such a well-known concept continues to evolve There will undoubtedly be more progress in the domain of smart pointers or maybe, smart resources, but just as important is the quality of smart pointers that are used today It's survival of the fittest, and that's why people are using Smart_ptr The Boost smart pointers are a fine, assorted selection of delicious software chocolate, and I eat them regularly (you should, too) We'll soon see some of them become part of the C++ Standard Library, as they have been accepted into the Library Technical Report
Trang 5Endnotes
15 Boost.Bind is just such a library
How Does the Conversion Library Improve Your Programs?
Understandable, maintainable, and consistent polymorphic conversions
Static downcasting using safer constructs than static_cast
Range-preserving numeric conversions that ensure correct value logic and less time debugging
Correct and reusable lexical conversions that lead to less time coding
The versatility of C++ is one of the primary reasons for its success, but sometimes also a formidable source of headaches because of the complexity of certain parts of the language For instance, the rules for numeric conversions and type promotions are far from trivial Other conversions are trivial, but tedious; how many times do
we need to write a safe function[1] for converting between strings and ints, doubles and strings, and so on? Conversions can be problematic in every library and
program you write, and that's how and why the Conversion library can help It provides facilities that prevent dangerous conversions and simplify recurring
conversion tasks
[1]
To avoid using sprintf and its ilk
The Conversion library consists of four cast functions that provide better type safety (polymorphic_cast), better efficiency with preserved type safety
(polymorphic_downcast), range-checked numeric conversions (numeric_cast), and lexical conversions (lexical_cast) These cast-like functions share the semantics of the C++ cast operators Like the C++ cast operators, these functions have an
important quality that, together with type safety, sets them apart from C-style casts: They unambiguously state the programmer's intent.[2] The importance of the code
we write goes far further than its implementation and present behavior More
important is to clearly convey our intents when writing it This library makes it somewhat easier by extending our C++ vocabulary
[2]
They can also be overloaded, which sometimes makes them superior to the C++ cast operators