Introducing Boost
According to the Boost website, Boost is "a repository of free, portable, peer-reviewed C++ libraries. Boost acts as a proving ground for new libraries, particularly those that work well with the ISO C++ Standard library." But Boost is more than a collection of libraries. It is also the focus of a rapidly growing community of developers who create, use, and discuss the Boost libraries. Beyond making these excellent libraries available, Boost also offers a place to learn. The libraries are examples of solid library design in a world that seldom seems to look further than the next release. Joining the discussions on the Boost mailing list (either actively or just by listening) is a good way to improve your understanding of library design problems and solutions. Boost also provides a rapidly growing Boost-Users mailing list that focuses on questions related to using the libraries.
The quality and the technological standard of the Boost libraries are amazing. And the Boost portability standard ensures that when you move your code to another platform, you'll know the libraries will still work. The current release, Boost 1.25.0, consists of libraries ranging from smart pointers to regular expressions to a portable threading library. Boost currently supports 35 libraries, all of which have been thoroughly reviewed by community members. These libraries can be freely used, and many of them are being used in commercial applications.
Boost is one of the strongest C++ communities. Among the 2,000 members are many of the world's top C++ programmers. Members stay involved because they like working with some of the best minds in programming. They also know their efforts are bound to have an impact on the C++ community, since much of what you see in Boost is a strong candidate for inclusion in the next C++ Standard.
The best way to get acquainted with Boost is to tour the Boost libraries. In this article, I'll introduce you to the Boost smart pointer library smart_ptr
. smart_ptr
is a good example of the innovation and sound design you'll find in Boost. I encourage you to visit the Boost website (www.boost.org) for more on the other 34 libraries in the Boost collection.
Smart Pointers
One of the smaller (size-wise) Boost libraries is smart_ptr
. smart_ptr
is one of the libraries that I think is going to end up in the C++ Standard. This article discusses the Boost smart_ptr
library. But first, I'll begin with a brief introduction to smart pointers.
30-Second Introduction to Smart Pointers
Smart pointers are classes that store pointers to dynamically allocated (heap) objects. They behave much like built-in C++ pointers except that they automatically delete the object pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects. They can also be used to keep track of dynamically allocated objects shared by multiple owners.
Actually, smart pointers can do more, such as handle thread safety, provide copy-on-write, ensure protocols, and provide remote communication services. There are ways to create generic smart pointers for these ESPs (Extremely Smart Pointers), but those won't be covered here. (See [1] for in-depth coverage of this topic. By the way, Alexandrescu is currently considering submitting his C++ library Loki to Boost.)
Most uses of smart pointers are for lifetime control, period. They implement operator->
and operator*
to yield the raw pointer, allowing the smart pointer to look like an ordinary pointer.
One such class comes with the standard library: std::auto_ptr
. It is designed to handle ownership of a resource, but lacks support for reference counting and arrays. Also, std::auto_ptr
transfers ownership when it is copied. In many cases, you need more and/or different functionality. Enter the smart_ptr
classes.
smart_ptr Classes
The smart pointers in Boost are:
scoped_ptr
, which handles sole ownership of single objects; unlikestd::auto_ptr
,scoped_ptr
cannot be copiedscoped_array
, which is similar toscoped_ptr
, but handles arraysshared_ptr
, which allows object ownership to be sharedshared_array
, which allows sharing of ownership for arrays
scoped_ptr
The scoped_ptr
smart pointer differs from std::auto_ptr
in that it doesn't transfer ownership. In fact, it explicitly forbids any attempt to do so! This is important for any scenario where you need to be sure that there is only ever one owner to a pointer. Without scoped_ptr
, you would probably be inclined to use std::auto_ptr
, but take a look at the following code:
auto_ptr<string> MyOwnString (new string("This is mine to keep!")); auto_ptr<string> NoItsMine(MyOwnString); cout << *MyOwnString << endl; // Boom
This code will obviously fail, since ownership of the string has been transferred to NoItsMine
. This is not a design flaw in std::auto_ptr
it is a feature. However, when you need MyOwnString
to be just that, you'll want to use scoped_ptr
:
scoped_ptr<string> MyOwnString (new string("This is mine to keep for real!")); // Compiler error - there is no copy constructor. scoped_ptr<string> TryingToTakeItAnyway (MyOwnString);
The scoped_ptr
accomplishes this behavior by inheriting from boost::noncopyable
(found in the library Boost.utility
). The non-copyable class declares the copy constructor and the assignment operator as private.
scoped_array
scoped_array
is obviously the equivalent of scoped_ptr
, but for arrays. You'll get no help from the standard library here unless of course you're using std::vector
, which is the right way to go in most cases.
The usage is similar to scoped_ptr
:
> typedef tuples::tuple<string, int> ArrayTuple; scoped_array<ArrayTuple> MyArray(new ArrayTuple[10]); tuples::get<0>(MyArray[5]) = "The library Tuples is also part of Boost";
Tuples are collections of elements for example pairs, triples, and quadruples. A typical use of tuples is returning multiple values from a function. The Boost Tuple Library can be thought of as an extended generalization of the standard library's pair, and it currently works with up to 10 tuple elements. It supports tuple streaming, comparison, assignment, unpacking, and more. For more information about the Boost Tuple Library, see [3].
As the scoped_array
goes out of scope, delete[]
will be properly called. This eliminates a common mistake, namely invoking the wrong operator delete
.
shared_ptr
Here's an offering that you won't find in the standard library a reference-counted smart pointer. Most of you have rolled your own smart pointers, and there is plenty of literature regarding the issues involved in reference counting. One of the most important details is how the reference count is implemented intrusive, which means that you add support to the classes being reference counted, or non-intrusive, which means you don't. The Boost shared_ptr
is non-intrusive, and the implementation uses a reference counter allocated from the heap. There has been much discussion about providing parameterized strategies to best suit any given situation, but in the end it was decided against to focus on usability. Don't expect the discussions to go away though.
The usage of shared_ptr
does what you'd expect: it takes responsibility of deleting the pointee when there are no more users of the instance, and it shares its pointee freely.
void PrintIfString(const any& Any) { if (const shared_ptr<string>* s = any_cast<shared_ptr<string> >(&Any)) { cout << **s << endl; } } int main(int argc, char* argv[]) { std::vector<boost::any> Stuff; shared_ptr<string> SharedString1 (new string("Share me. By the way, Boost.any is another useful Boost library")); shared_ptr<string> SharedString2 (SharedString1); shared_ptr<int> SharedInt1 (new int(42)); shared_ptr<int> SharedInt2 (SharedInt1); Stuff.push_back(SharedString1); Stuff.push_back(SharedString2); Stuff.push_back(SharedInt1); Stuff.push_back(SharedInt2); // Print the strings for_each(Stuff.begin(), Stuff.end(), PrintIfString); Stuff.clear(); // The pointees of the shared_ptr's // will be released on leaving scope return 0; }
The any
library offers a way to store just about anything [2] [4]. The requirements on contained types are that they are CopyConstructible
, the destructor must not throw, and they should typically be Assignable
. How do we store and pass "anything"? Indiscriminate types (read void*
) can refer to just about anything, but that means throwing type safety (and knowledge) out the door. Using any
, type safety is preserved. All types satisfying the requirements for any
can be assigned, but extraction demands knowing the exact type. any_cast
is the key for unlocking the value held by any
. any_cast
works like dynamic_cast
casts to pointer types succeed or fail by returning a null pointer whereas casts to reference types throw an exception (bad_any_cast
) on failure.