Smart Pointers Overview — Volkswagen Pointer I

19 Июн 2015 | Author: | Комментарии к записи Smart Pointers Overview — Volkswagen Pointer I отключены
Volkswagen Pointer I

Smart Pointers Overview

An approach to a classic C++ idea

by M. Dlugosz

My interest in smart grew out of my interest in multithreading. data is being passed threads, such as in the case of to an asynchronous function, there is a ownership issue.

But, in more mundane programming, the arises. Strings are a case in in the old days of C, returning a string a function was a royal pain. were numerous approaches to such as returning a dynamically string that must be freed by the caller, returning a buffer that gets with each call, or a destination buffer and length as With discipline from the involved, and with a little of getting burned a few times, it worked. But, it’s a drag on the programmer’s attention ought to be better spent on things.

With a modern class, we have the power we see in other high-level languages, returning a string from a is just as simple as returning an You just return the thing, no no fuss.

So, reference counting via pointers is the way to go in C++. There are widespread uses of this once a reusable smart component is available. The challenge is to use C++ (in particular, templates) to produce a smart pointer class stands up to some rather design requirements.

Reference vs. Garbage Collection

One day a student to Moon and said: I understand how to a better garbage collector. We keep a reference count of the to each cons.

Moon told the student the following

One day a student came to Moon and ‘I understand how to make a better collector…

—An AI Koan, courtesy of The New Dictionary a.k.a. The Jargon

Reference counting has a problem recursive or circular references. As the from an ancient LISP shows, the problem has long understood. Garbage collection is not to this particular problem, so of garbage collection in languages to ridicule the proponents of reference

But, garbage collection is not at all the thing. C++ has a very distinctive that sets it apart other contemporary object-oriented value semantics. In Smalltalk, Object Pascal, and Java, for objects have reference Assigning one variable to another is a operation, not an operation on the whole

In contrast, the assignment operator in C++ on the whole object, and pointers, if are treated separately. This idea gave us constructors and and led to the wonderful concept of resource as initialization. This is one of the great of the C++ philosophy, and we should embrace it than try to circumvent it.

The meaning of garbage collection in C++ is to be a of infinite memory. If the system can that a particular object never be used again, it can it out to devnul for all anyone cares. is, the destructor is not called.

Reference on the other hand, is a system of exactly when the last use of an is extinguished, and calling the destructor at precise point in the execution.

a class that represents a on the screen. When the program is using that window, why it remain on the screen? Because the collector hasn’t noticed For the concept of resource acquisition as we must have timely Objects are not just memory to be when we run low—objects in C++ are files, communication channels, and other with real effects due to

Destruction when the last is finished is like a room the last one out turns off the lights. The is shared among all users of the Putting this logic in the itself is a very O-O concept, the object is taking care of It’s like the light knowing when the last left the room, and turning off.

The End of Innocence

A serious counting pointer in C++ needs to this exact problem. The news is, I’ve solved it. My pointer system handles references.

That’s a doozie, but not the problem. You can have multiple to the same object in C++, the values of the pointers don’t

Here, p1 and p2 point to the same but can’t be the same address. smart pointers in the same you find that they are of types, and hold different yet must realize that have a common lifetime and cooperate with ownership in the same manner as two smart of the same type and same value.

If that’s not enough, const pointers. Or rather, to constants. Point is, const can be in two places within a pointer with different meanings.

The pointers need to have same concept, with checking of constness in both

If that’s not enough, there are two of behavior that are often with smart pointers. One is aliasing, where two smart reference the same object and a in one is seen by the other. On the other you may prefer copy-on-write behavior, allows efficient passing and of large values, and full semantics. This is what you of a string class. Naturally, we both types of behavior.

As a minor issue, but one that up during my experiments, the smart should support the NULL

Next, the smart pointer be able to accommodate data that were never with smart pointers in In particular, classes from libraries should be handled by smart pointers.

Oh, and remember the need I mentioned for smart They must be thread

The Tour Starts Here

article explores the design of a pointer system that the above requirements. Future will explore individual and their impact on the implementation in depth. But you don’t have to complete source code is from my web site.

What Do and Cows Have To Do With It?

are three different kinds of pointers collectively called . that fundamentally behave in the way you a smart pointer should. they all have an operator- provides access to the underlying

Note: the examples use the parenthesis of initialization (formally called initialization) rather than the ‘=’ (copy initialization) because the keyword on the constructor requires You could write the regular with direct initialization e.g. C*p(new C); but most don’t write it that

As you can see, handle is a template allows you to specify the type of the pointer. There are actually such templates: handle . . and cow .

The const_handle is just like a but provides read-only access. the analogous case with pointers:

The smart pointer analogously to the regular pointer. operator- on the const_handle returns a pointer, while operator- on the handle returns an unqualified Meanwhile, you may implicitly convert a to the same kind of handle. but not This is done by using

A regular handle can do anything a can, and then some. So it a derived class is a perfectly thing to do. The less-restricted form of hides the one in the base class.

that both versions of are const functions. Why isn’t the one written

instead? Because have two kinds of const to them. The constness of the thing controlled by the pointer is distinct the pointer itself. The dereference is a const function because doesn’t change the value of the The const on member functions of the pointer are used to distinguish other dimension of constness, as

Note: the same applies to but my handle classes don’t this.

So, the difference between and handle is clear enough. So of cow ?

The requirements stated a need for alias and copy-on-write functionality. If you use on a handle. you get the object, which may be to by other handles. It doesn’t to handle. The operator- just the controlled object, and doesn’t whether other pointers are pointing to it. This is the simplest and straightforward implementation, and corresponds to the requirement. This is also how pointers work.

The other is copy-on-write. In this case, an would check to see if there other references, and if so, perform a first. The operator- always a unique value, never a value. A different implementation of calls for another class. The cow is also derived from and works the same as handle. for the copy-on-write checking.

There is no between the two behaviors if the object is not to, so only a single kind of exists. Notice that any use of on the cow handle will cause to be triggered—it can’t tell if you are a const member or not, so it the worst. Because of this, it is efficient to use the const_handle when you are not to actually change the object. you can pass a cow to any function taking a parameter.

What You Don’t Own You Can

I’m tinkering with an experimental programming framework called One of the design goals is to totally all ownership issues. I hate the way MFC and OWL make you new various objects, and make it clear when is held by some enclosing and when it isn’t. Most just give up and never any framework object. This duplicate frees, but causes and resource leaks, too. smart pointers provide the

I decided that all relationships objects in Tomahawk, including things as child windows, GDI and system resources would be through reference-counting, and most (if not functions work with semantics and lazy copying the way a string class works). In no ownership problems, ever.

were minor usability that I quickly faced, and the smart pointer design A major problem remained, The dreaded circular reference Now, thanks to foreshadowing, you I solved this problem, and is in the reason I’m writing this But when I first started, I like I was pinned against a wall, trapped between design goals.

The problem is that of child More generally, it applies to any set of that send messages to other, but I studied the problem with the case of child The parent window points to the and the child needs a pointer to the parent. Yes, I tried without one or the other as actual using child ID numbers or some such. But it was inefficient and generalize. And, there was a problem with multithreaded

My attempts to keep track of the window using other and special considerations eventually to the concept of the baro handle. stands for ba ck- r eference o bject.

how the idea works. A normal pointer does two things: It access to another object, and it controls the lifetime of that But, circular references problems, because two objects refer to each other own each other. That’s not we’re trying to model, The framework owns both and the parent owns the child, but the doesn’t own the parent. But, the needs to refer to the parent.

people who use smart pointers back to regular dumb when they need to to the object without participating in object’s lifetime. But that to dangling pointers. Suppose the window were destroyed by GUI but the child window, on its own thread, was in the of processing a message and, it knows its being destroyed tries to refer to the parent.

The solution is not to use dumb pointers, but to use a kind of smart pointer.

A works with the smart-pointer so is aware of the object’s lifetime, but not own the object. That is, outstanding handles to an object don’t the object from destructing. all cow s, handle s, and const_handle s to the object the object is destroyed. The baro don’t count.

You might that a baro implements yet form of operator- that for the continued existence of the object returning. But that’s not the best because of the threading issues. if you call foo through a baro and before foo returns, another drops the last regular destroying the object? In short, a doesn’t own the object, but when the is actually in use, the object need to be owned, until the returns.

Instead, a baro doesn’t an operator- at all! You cannot a baro. What you can do is construct a const_handle. or cow using a baro The handle refers to the same as the baro. assuming there was and hence gives you ownership. You use the object through the handle. and you’re done, you let that go out of scope.

The error checking place during the construction of a from a baro. and this throws an exception if the object no exists. Once the handle is the code can be sure of the object’s existence. Is that elegant or

So back to our pathological window The parent owns the child, but the only borrows the parent. The is destroyed when you click in the [x] in the of the window, but the child doesn’t that yet because of the inherent problems of multithreaded programs. when the child needs to a message to the parent, it uses like in the above sample. Its pointer is a baro. and upon to create a useable handle it, finds out in no uncertain terms the parent no longer exists.

The pointer is always caught at There are no more stray writes or other mysterious in the code. This is useful in C++ to exceptions, and much more than tracking and nulling all references when an object is

There is also a const_baro which only allows the of a const_handle. A regular baro can any kind of handle. This a total of five related pointers in this system.

and Converting

Pointers can be converted to class pointers, and can even be again, if you dare.

I need to a handle to a function that a handle. That’s rather actually, thanks to template functions. The std::auto_ptr in the ANSI/ISO library does exactly Unfortunately, the Microsoft compiler implement template member

I came up with two work-around’s. I made a handle to a derived another class, rather a simple handle. This a template to create a class from handle (what I wanted) that also an implicit conversion function to as one of its members. Yuck, but it works

A solution that doesn’t on the declaration of the needed handle also led to the general suite of casts.

Look at the correspondence between and built-in pointers using system:

Basically, there are forms that correspond to the casting operators, with the meanings. The thing in the angle can be any of the five handle types on any type, and works if the const of handles are satisfied and if the corresponding cast works on the referenced

To get around the lack of implicit (a flaw in the compiler, not in my design), I an implicit_handle_cast form that performs casts that have been done It won’t do anything dangerous—only me to convert derived-to-base.

Because and handles can be converted, it’s for two handles of different types to to the same object. In the above both hder and hbase to the same object, and the lifetime need to deal with

Lost Found

It’s of course, to construct a smart from a regular pointer. if that object is already in the then it should understand there are other smart to this object already, and with them.

For example, we everyone would write smart pointers alone:

but sometimes happens is this:

In the case, where the regular is extracted from the smart and then another smart is constructed, it should have the result as the first case, one smart pointer is constructed from another. Naturally, needs to handle derived-to-base issues at the same time. If the count is part of the object, lost found effect is no big But when handling objects were not designed for reference and in coping with the derived/base issues, we need to make the design covers this


In order for smart in C++ to solve specific issues of and ownership, they need to with the exact situations make ownership issues so A serious smart pointer must deal with a of important issues. But the resulting is wonderful to have.


Other articles of the category "Pointer":

Our partners
Follow us
Contact us
Our contacts

Born in the USSR

About this site

For all questions about advertising, please contact listed on the site.

Volkswagen all cars catalog with specifications, pictures, ratings, reviews and discusssions about cars Volkswagen.