Upload
ppd1961
View
3.161
Download
2
Tags:
Embed Size (px)
DESCRIPTION
I talk on a variety of Smart Pointers in this lecture as Memory Management idioms in C++.
Citation preview
October 28, November 4,
December 2, 2005
Beware of Pointers!
Dr. Partha Pratim DasInterra Systems (India) Pvt. Ltd.
Resource Management Series
04/12/23 22
Resource Management
• Beware of Pointers – Be Aware of Smart Pointers– Typifying pointers– auto_ptr
• Issues in Resource Management – Object Lifetime Management
• Singleton Objects
– Non-Memory Resource Management• File Pointer, GUI, Socket, Lock, …• Proxy Classes
• String Management• Glimpses from Boost Library / C++0x TR1
04/12/23 33
Motivation
• Imbibe a culture to write “good” C++ code – Correct: Achieves the functionality– Bug free: Free of programming errors– Maintainable: Easy to develop & support– High performance: Fast, Low on memory.
“C++ is an abomination to society, and is doubtlessly responsible for hundreds of millions of lost hours of productivity.”
– Space Monkey as posted on kuro5him.org
04/12/23 44
Agenda
• Raw Pointers – A recap– Operations– Consequences of not being an FCO– Pointer Hazards
• A Pointer-free World– Pointers vis-à-vis Reference– Quick Tour of Pointer-Free Languages
04/12/23 55
Agenda
• Smart Pointers in C++– Policies
• Storage
• Ownership
• Conversion– Implicit Conversions
– Null Tests
– Checking
– Other Design Issues
– Performance Issues
– Smart Pointers in Practice
“Understanding pointers in C is not a skill, it's an aptitude…”
– Joel Spolsky in “Joel on Software - The
Guerrilla Guide to Interviewing”
04/12/23 66
Agenda
• std::auto_ptr – The Philosophy– Standard Interface & Sample Implementation– Using Idioms– History– Portability– Pitfalls
• References & Credits
04/12/23 77
Raw Pointers
A Raw Deal?A Raw Deal?
04/12/23 88
What is a Raw Pointer?
• A pointer is an object that gives the address of another object (Data or Function).
• The value of a pointer is a memory location.• Pointers are motivated by indirect addresses in
machine languages.• Pointers can be typed. • Pointers can be typeless.• Alternate names: Bald, Built-in, Dull, Dumb,
Native, Primitive.
“Pointers: the GOTO of data structures”– P.J. Moylan in “The case against C”
Pointered Languages: Pascal, Ada, C, C++
04/12/23 99
What is a Raw Pointer?• Raw Pointer Operations
– Dynamic Allocation (result of) or operator&– Deallocation (called on)– De-referencing operator*– Indirection operator->– Assignment operator=– Null Test operator! (operator== 0) – Comparison operator==, operator!=, …– Cast operator(int), operator(T*)– Address Of operator&– Address Arithmetic operator+, operator-, operator++, operator--, operator+=, operator-=
– Indexing (array) operator[]
04/12/23 1010
What is a Raw Pointer?
• Typical use of Pointers– Essential – Link (‘next’) in a data structure
– Inessential – Apparent programming ease• Passing Objects in functions: void MyFunc(MyClass *);
• ‘Smart’ expressions: while (p) cout << *p++;
• Is not a “First Class Object” – An integer value is a FCO
• Does not have a “Value Semantics”– Cannot COPY or ASSIGN at will
• Weak Semantics for “Ownership” of pointee
04/12/23 Source: Modern C++ Design 1111
Ownership Issue of Pointers
• Ownership Issue – ASSIGN problem
• Memory Leaks!
// Create ownership
MyClass *p = new MyClass;
// Lose ownership
p = 0;
04/12/23 Source: Modern C++ Design 1212
Ownership Issue of Pointers
• Ownership Issue – COPY problem
• Double Deletion Error!
// Create ownership
MyClass *p = new MyClass;
// Copy ownership – no Copy Constructor!
MyClass *q = p;
// Delete Object & Remove ownership
delete q;
// Delete Object – where is the ownership?
delete p;
04/12/23 Source: C++ Templates - The Complete Guide 1313
Ownership Issue of Pointers
• Ownership Issue – SCOPE problem
• Memory Leaks due to stack unrolling!
void MyAction() {
// Create ownership
MyClass *p = new MyClass;
// What if an exception is thrown here?
p->Function();
// Delete Object & Remove ownership
delete p;
}
04/12/23 Source: C++ Templates - The Complete Guide 1414
Ownership Issue of Pointers
• try-catch solves this case
void MyAction() {
MyClass *p = 0;
try {
MyClass *p = new MyClass;
p->Function();
}
catch (…) {
delete p; // Repeated code
throw;
}
delete p;
}
04/12/23 Source: C++ Templates - The Complete Guide 1515
Ownership Issue of Pointers
• Exceptional path starts dominating regular path
void MyDoubleAction() {
MyClass *p = 0, *q = 0;
try {
MyClass *p = new MyClass;
p->Function();
MyClass *q = new MyClass;
q->Function();
}
catch (…) {
delete p; // Repeated code
delete q; // Repeated code
throw;
}
delete p;
delete q;
}
04/12/23 1616
Pointer Hazards
• Pointer issues dominate all Memory Errors in C++– Null Pointer Dereference
– Dangling pointers
– Double Deletion Error
– Allocation failures
– Un-initialized Memory Read
– Memory Leaks
– Memory Access Errors
– Memory Overrun
– Exceptional Hazards
“If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization.”
– Weinberg's Second Law
04/12/23 Source: Softw. Pract. Exper. 2000; 30:775–802 1717
Pointer Hazards:PREfix Simulation Results
Warning Mozilla 1.0, Apr-98
Apache 1.3beta5, Feb-98
GDI Demo NuMega
Using uninitialized memory 26.14 45.00 69.00Dereferencing uninitialized pointer 1.73 0.00 0.00Dereferencing NULL pointer 58.93 50.00 15.00Dereferencing invalid pointer 0.00 5.00 0.00Dereferencing pointer to freed memory 1.98 0.00 0.00Leaking memory 9.75 0.00 0.00Leaking a resource (such as a file) 0.09 0.00 8.00Returning pointer to local stack variable 0.52 0.00 0.00Returning pointer to freed memory 0.09 0.00 0.00Resource in invalid state 0.00 0.00 8.00Illegal value passed to function 0.43 0.00 0.00Divide by zero 0.34 0.00 0.00Total 100.00 100.00 100.00Language C++ C CLines of Code 540613 48393 2655
04/12/23 1818
A Pointer-free World
Reality or Utopia?Reality or Utopia?
04/12/23 Source: http://www.goingware.com/tips/parameters/ 1919
How to deal with an Object?
• The object itself – – by value
• Performance Issue• Redundancy Issue
• As the memory address of the object – – by pointer
• Lifetime Management Issue• Code Prone to Memory Errors
• With an alias to the object – – by reference
• Good when null-ness is not needed• Const-ness is often useful
04/12/23 2020
Pointers vis-à-vis Reference
• Use ‘Reference’ to Objects when– Null reference is not needed– Reference once created does not need to change
• AvoidsAvoids– The security problems implicit with pointersThe security problems implicit with pointers– The (pain of) low level memory management The (pain of) low level memory management
(i.e. delete)(i.e. delete)
• W/o pointer – UseW/o pointer – Use– Garbage CollectionGarbage Collection
“Avoid working with pointers.
Consider using references instead.”“Avoiding Common Memory Problems in C++” –
MSDN Article
04/12/23 2121
Pointers-free Languages
• What is a pointer-free language?– There is no high-level construct to directly
access the address of an Object– Internally, the language may use indirect
addressing– Some pointer-free languages expose pointers
through ‘illegal’ or ‘unsafe’ means
04/12/23 2222
Pointers-free Languages
• Fortran– Use arrays to link
• VB / VB.Net– Use Win32 API / Un-documented Functions to
create pointers (link data structures)
04/12/23 2323
Pointers-free Languages
• Java– Internally every Java object is accessed through
a pointer– Explicit pointer in native method calls
“Most studies agree that pointers are one of the primary features that enable programmers to put bugs into their code. Given that structures are gone, and arrays and strings are objects, the need for pointers to these constructs goes away .”
– The Java Language Environment: A White Paper, Sun 1995. (http://java.sun.com)
04/12/23 Source: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_A_2.asp
2424
Pointers-free Languages
• C#– Pointers omitted in core language– Can still be used in “Unsafe Code”
• Modifier “unsafe”
• Interfacing with the underlying operating system
• Accessing a memory-mapped device
• Implementing a time-critical algorithm
• …
04/12/23 Source: http://digitalmars.com/d/index.html 2525
Pointers-free Languages
• D Programming Language– Dynamic arrays instead of pointers– Reference variables / objects instead of pointers– GC instead of explicit memory management– Vastly reduced need for pointers
“Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary.”
– Revised Report on Scheme, 1991
04/12/23 Source: http://www.daimi.au.dk/~beta/ 2626
Pointers-free Languages
• SmallTalk• Eiffel (www.eiffel.com)
– Has no pointers only – object references. – Exact referencing mechanism does not matter. – In the expression x.f, the reference x might be:
• A pointer to an object in the same address space, or • An Internet address of an object.
– References enable the location and access method of an object to be transparent.
• BETA• …
“A language that doesn't affect the way you think about programming, is not worth knowing.” – Alan Perlis
04/12/23 2727
Smart Pointers in C++
The Smartness …The Smartness …
04/12/23 2828
What is Smart Pointer?
• A Smart pointer is a C++ object • Stores pointers to dynamically allocated (heap /
free store) objects• Improves raw pointers by implementing
– Construction & Destruction
– Copying & Assignment
– Dereferencing:• operator–>
• unary operator*
• Grossly mimics raw pointer syntax & semantics
04/12/23 2929
What is Smart Pointer?
• Performs extremely useful support tasks– RAII – Resource Acquisition is Initialization Idiom – Selectively disallows “unwanted” operations
• Address Arithmetic
– Lifetime Management• Automatically deletes dynamically created objects at
appropriate time• On face of exceptions – ensures proper destruction of
dynamically created objects• Keeps track of dynamically allocated objects shared by
multiple owners
– Concurrency Control
04/12/23 Source: http://www.informit.com/articles/article.asp?p=31529&rl=1
3030
A Simple Smart Pointertemplate <class T> class SmartPtr { public:
// Constructible. No implicit conversion from Raw ptr explicit SmartPtr(T* pointee): pointee_(pointee); // Copy Constructible SmartPtr(const SmartPtr& other);// Assignable SmartPtr& operator=(const SmartPtr& other); // Destroys the pointee ~SmartPtr(); // Dereferencing T& operator*() const { ... return *pointee_; } // Indirection T* operator->() const { ... return pointee_; }
private: T* pointee_; // Holding the pointee
};
04/12/23 Source: http://www.informit.com/articles/article.asp?p=31529&rl=1
3131
A Smart Pointer mimics a Raw Pointer
class MyClass { public:
void Function(); };
// Create a smart pointer as an objectSmartPtr<MyClass> sp(new MyClass);
// As if indirecting the raw pointer sp->Function(); // (sp.operator->())->Function()
// As if dereferencing the raw pointer(*sp).Function();
04/12/23 3232
Smart Pointer Member Functions
• Potential Name Conflict with pointee type
• Method at namespace
SmartPtr<Printer> spRes = ...;
spRes->Acquire(); // acquire the printer ... print a document ...
spRes->Release(); // release the printer
spRes.Release(); // release the pointer to the printer
// GetImpl returns the pointer object stored by SmartPtr
template <class T> T* GetImpl(SmartPtr<T>& sp);
// GetImplRef returns a reference to the pointer stored by SmartPtr
template <class T> T*& GetImplRef(SmartPtr<T>& sp);
// Reset resets the underlying pointer to another value,
// Releases the previous one
template <class T> void Reset(SmartPtr<T>& sp, T* source);
// Release releases ownership of the smart pointer
template <class T> void Release(SmartPtr<T>& sp, T*& destination);
04/12/23 Source: More Effective C++ 3333
A Smart Pointer Use-Case
• A Distributed System • Objects can be
– Local: Access is simple & fast
– Remote: Access is involved & costly
• How can an application code handle local & remote objects uniformly, robustly and elegantly?
04/12/23 Source: More Effective C++ 3434
A Smart Pointer Use-Casetemplate<class T> // Template for Smart
class DBPtr { // Pointers to objects
public: // in a distributed DB
DBPtr(T *realPtr = 0); // Create a Smart ptr to a
// DB object given a local
// raw pointer to it
DBPtr(DataBaseID id); // Create a smart ptr to a
// DB object given its
// unique DB identifier
... // other smart ptr functions
};
04/12/23 Source: More Effective C++ 3535
A Smart Pointer Use-Caseclass Tuple { // class for database tuplespublic: ...
void displayEditDialog(); // present a graphical// dialog box allowing a// user to edit the tuple
bool isValid() const; // return whether *this}; // passes validity checktemplate<class T> // template class for
making class LogEntry { // log entries whenever a
Tpublic: // object is modified
// Begins log entryLogEntry(const T& objectToBeModified);// Ends log entry~LogEntry();
};
04/12/23 Source: More Effective C++ 3636
A Smart Pointer Use-Casevoid editTuple(DBPtr<Tuple>& pt) {
LogEntry<Tuple> entry(*pt); // make log entry // for this edit
// Repeat display edit dialog for valid values
do {
pt->displayEditDialog();
} while (pt->isValid() == false);
}
• Application is oblivious of Object location • No separate calls to start and stop logging• Robust against exception
04/12/23 Source: C++ Without Memory Errors 3737
The Smartness …
• It always points either to a valid allocated object or is NULL.
• It deletes the object once there are no more references to it.
• Fast. Preferably zero de-referencing and minimal manipulation overhead.
• Raw pointers to be only explicitly converted into smart pointers. Easy search using grep is needed (it is unsafe).
• It can be used with existing code.
04/12/23 Source: C++ Without Memory Errors 3838
The Smartness …
• Programs that don’t do low-level stuff can be written exclusively using this pointer. No Raw pointers needed.
• Thread-safe. • Exception safe. • It shouldn’t have problems with circular references.
04/12/23 3939
Smart Pointers in C++
Storage PolicyStorage Policy
04/12/23 4040
3–Way Storage Policy
• The Storage Type (T*)– The type of pointee.
• Specialized pointer types possible: FAR, NEAR.
– By “default” – it is a raw pointer. • Other Smart Pointers possible – When layered
• The Pointer Type (T*)– The type returned by operator–>
• Can be different from the storage type if proxy objects are used.
• The Reference Type (T&)– The type returned by operator*
04/12/23 4141
Smart Pointers in C++
Ownership Management PolicyOwnership Management Policy
04/12/23 4242
Ownership Management Policy
• Smart pointers are about ownership of pointees
• Exclusive Ownership– Every smart pointer has an exclusive ownership of the pointee
– Three common flavors for this policy• Destructive Copy – std::auto_ptr
• Deep Copy [Raw Pointers have Shallow Copy]
• COW: Copy-on-Write
• Shared Ownership– Ownership of the pointee is shared between Smart pointers
– Track the Smart pointer references for lifetime• Reference Counting
• Reference Linking
04/12/23 4343
Ownership Policy: Destructive Copy
• Exclusive Ownership Policy• Transfer ownership on copy• Source Smart Pointer in a copy is set to NULL• Available in C++ Standard Library
– std::auto_ptr
• Implemented in – Copy Constructor– operator=
04/12/23 4444
Ownership Policy: Destructive Copy
template <class T> class SmartPtr {
public:
SmartPtr(SmartPtr& src) { // Src ptr is not const
pointee_ = src.pointee_; // Copy
src.pointee_ = 0; // Remove ownership for src ptr
}
SmartPtr& operator=(SmartPtr& src) { // Src ptr is not const
if (this != &src) { // Check & skip self-copy
delete pointee_; // Release destination object
pointee_ = src.pointee_; // Assignment
src.pointee_ = 0; // Remove ownership for src ptr
}
return *this; // Return the assigned Smart Pointer
} ...
};
04/12/23 4545
Ownership Policy: Destructive Copy – The Maelstrom Effect
• Consider a call-by-value
• Display acts like a maelstrom of smart pointers:– It sinks any smart pointer passed to it. – After Display(sp) is called, sp holds the null pointer.
• Lesson – Pass Smart Pointers by Reference.• Smart pointers with destructive copy cannot
usually be stored in containers and in general must be handled with care.
void Display(SmartPtr<Something> sp); ...
SmartPtr<Something> sp(new Something);
Display(sp); // sinks sp
STL Containers need FCO.
04/12/23 4646
Ownership Policy: Destructive Copy – Advantages
• Incurs almost no overhead.
• Good at enforcing ownership transfer semantics. – Use the “maelstrom effect” to ensure that the function takes
over the passed-in pointer.
• Good as return values from functions. – The pointee object gets destroyed if the caller doesn't use the
return value.
• Excellent as stack variables in functions that have multiple return paths.
• Available in the standard – std::auto_ptr. – Many programmers will get used to this behavior sooner or
later.
04/12/23 4747
Ownership Policy: Deep Copy
• Exclusive Ownership Policy– Only one Smart Pointer for every pointee object
• Copy the pointee Object when copying the Smart Pointer
– Does it have any worth over call-by-value?
• Implemented in – Copy Constructor– operator=
04/12/23 4848
Ownership Policy: Deep Copy – Object Slicing
• Naïve implementation
• Does not work for polymorphic objects– Object Slicing: only ‘base’ part of a derived class
object is copied
template <class T>
class SmartPtr {
public:
SmartPtr(const SmartPtr& other): pointee_(new T(*other.pointee_))
{ ... }
...
};
04/12/23 4949
Ownership Policy: Deep Copy – Transport Polymorphic Objects
• Smart implementation
• Safe mechanism to transport polymorphic objects
// Base class provides a prototype for Clone
class AbstractBase { ...
virtual Base* Clone() = 0;
};
// Derived class implements Clone
class Concrete : public AbstractBase { ... virtual Base* Clone() {
return new Concrete(*this);
}
};
04/12/23 5050
Ownership Policy: Copy-on-Write (COW)
• Exclusive Ownership Policy• Allow multiple Smart pointers to point to the
same pointee• Copy the pointee Object when it is written to
(Lazy Evaluation)• Smart pointers are not the best place to
implement COW– cannot differentiate between calls to const and non-
const member functions of the pointee object.
• COW is used in Meyers’ String Class.
04/12/23 5151
Ownership Policy: Reference Counting
• Shared Ownership Policy• Allow multiple Smart pointers to point to the
same pointee • A count of the number of Smart pointers
(references) pointing to a pointee is maintained• Destroy the pointee Object when the count
equals 0• Do not keep: raw pointers and smart pointers to
the same object.
04/12/23 5252
Ownership Policy: Reference Counting
• Variant Sub-Policies include– Non-Intrusive Counter
• Multiple Raw Pointers per pointee
• Single Raw Pointer per pointee
– Intrusive Counter
• Implemented in – Constructor
– Copy Constructor
– Destructor– operator=
04/12/23 5353
Ownership Policy: Reference Counting: Non-Intrusive Counter
• Additional count pointer per Smart Pointer.
• Count in Free Store
• Allocation of Count may be slow & wasteful because it is too small
04/12/23 5454
Ownership Policy: Reference Counting: Non-Intrusive Counter
• Additional count pointer removed.• But additional access level means slower speed.
04/12/23 5555
Ownership Policy: Reference Counting: Intrusive Counter
• Most optimized RC Smart Pointer• Cannot work for an already existing design• Used in COM
04/12/23 5656
Ownership Policy: Reference Linking
• Shared Ownership Policy• Allow multiple Smart pointers to point to the
same pointee • All Smart pointers to a pointee are linked on a
chain– The exact count is not maintained – only check if the
chain is null
• Destroy the pointee Object when the chain gets empty
• Do not keep: raw pointers and smart pointers to the same object.
04/12/23 5757
Ownership Policy: Reference Linking
• Overhead of 2 additional pointers
• Doubly-linked list for constant time:
– Append,
– Remove & Empty detection.
04/12/23 5858
Ownership Policy: Reference Management – Disadvantage
• Circular / Cyclic Reference– Object A holds a smart pointer to an object B.
Object B holds a smart pointer to A. Forms a cyclic reference.
• Typical for a Tree: Child & Parent pointers
– Cyclic references go undetected• Both the two objects remain allocated forever
• Resource Leak occurs.
– The cycles can span multiple objects.
04/12/23 5959
Ownership Policy: Cyclic Reference – Hack
The Hack• Use Smart
pointer from Parent to Child.
– “Data Structure” Pointers
• Use Raw pointer from Child to Parent.
– “Algorithm” Pointers
Smart Pointers “own”; Raw Pointers “disowns”?
04/12/23 Source: C++ Without Memory Errors 6060
Ownership Policy: Cyclic Reference – Solution
• Maintain two flavors of RC Smart Pointers– “Strong” pointers that really link up the data structure
(Child / Sibling Links). They behave like regular RC.– “Weak” pointer for cross / back references in the data
structure (Parent / Reverse Sibling Links)
• Keep two reference counts: – One for total number of pointers, and – One for strong pointers.
• While dereferencing a weak pointer, check the strong reference count.
– If it is zero, return NULL. As if, the object is gone.
04/12/23 6161
Smart Pointers in C++
Implicit ConversionImplicit Conversion
04/12/23 6262
Implicit Conversion
• Consider
• User-Defined Conversion (cast)
• User-unattended access to the raw pointer can defeat the purpose of the smart pointer
// For maximum compatibility this should work
void Fun(Something* p); ...
SmartPtr<Something> sp(new Something);
Fun(sp); // OK or error?
template <class T> class SmartPtr {
public:
operator T*() // user-defined conversion to T*
{ return pointee_; } ...
};
04/12/23 6363
Implicit Conversion: The Pitfall
• This compiles okay!!!
• Ambiguity Injection solves …
• Prefer Explicit Conversion over Implicit – Use GetImpl() & GetImplRef()
// A gross semantic error that goes undetected at compile time
SmartPtr<Something> sp; ...
delete sp; // Compiler passes this by casting to raw pointer
template <class T> class SmartPtr {
public:
operator T*() // User-defined conversion to T*
{ return pointee_; }
operator void*() // Added conversion to void*
{ return pointee_; } ...
};
“When in doubt, use brute force.” – K
en Thom
pson
04/12/23 6464
Smart Pointers in C++
Null TestsNull Tests
04/12/23 6565
Null Tests• Expect the following to work?
• Implicit conversion to:– T*– void *
• Implicit conversion Risky delete Ambiguity Injection Ambiguity causes compilation failures
SmartPtr<Something> sp1, sp2;
Something* p; ...
if (sp1) // Test 1: direct test for non-null pointer ...
if (!sp1) // Test 2: direct test for null pointer ...
if (sp1 == 0) // Test 3: explicit test for null pointer ...
04/12/23 6666
Null Tests
• Overload operator!template <class T> class SmartPtr {
public:
// Returns true iff pointee is NULL
bool operator!()
{ return pointee_ == 0; }
};
if (sp1) // Rewrite as if (!!sp1)
if (!sp1) // Works fine
if (sp1 == 0) // Does not work
04/12/23 6767
Smart Pointers in C++
Checking PolicyChecking Policy
04/12/23 6868
Checking Policy
• Applications need various degrees of safety:– Computation-intensive – optimize for speed.– I/O intensive –allows better runtime checking.
• Two common models: – Low safety / High speed (critical areas).– High safety / Lower speed.
• Checking policy with smart pointers: – Checking Functions
• Initialization Checking &• Checking before Dereferencing
– Error Reporting
04/12/23 6969
Checking Policy: Initialization Checking
• Prohibit a Smart Pointer from being NULL– A Smart Pointer is always valid
– Loses the ‘not-a-valid-pointer’ idiom
– How would default constructor initialize raw pointer?
template <class T> class SmartPtr {
public:
// Prohibit NULL for initialization
SmartPtr(T* p): pointee_(p) {
if (!p) throw NullPointerException();
} ...
};
04/12/23 7070
Checking Policy: Checking before Dereferencing
• Dereferencing a null pointer is undefined!
• Implemented in – Operator->– Operator*template <class T> class SmartPtr {
public: T& operator*() const { // Dereferencing
if (!pointee_) throw NullPointerException(); else return *pointee_; }
T* operator->() const { // Indirection if (!pointee_) throw NullPointerException(); else return pointee_; }
...
};
if (p) { /* Dereference & use p */ }
else { /* Handle null pointer condition */ }
04/12/23 7171
Checking Policy: Error Reporting
• Throw an exception to report an error
• Use ASSERT in debug build– Checking (debug) + Speed (release)
• Lazy Initialization – construct when needed– Operator->– Operator*
template <class T> class SmartPtr {
public: T& operator*() { // Dereferencing. No Constant
if (!pointee_) pointee_ = new T;return *pointee_; }
T* operator->() { // Indirection. No Constant if (!pointee_) pointee_ = new T;return pointee_; }
...
};
04/12/23 7272
Smart Pointers in C++
Other Design IssuesOther Design Issues
04/12/23 7373
What is a Smart Pointer?
• Smart Pointer Operations– Construction, Copy Construction operator new– Destruction operator delete– De-referencing operator*– Indirection operator->– Assignment operator=– Null Test operator! (operator == 0) – Comparison operator==, operator!=, …– Cast operator(int), operator(T*)– Address Of operator&– Address Arithmetic operator+, operator-, operator++, operator--, operator+=, operator-=
– Array operator new, operator delete, operator[]
04/12/23 7474
Other Design Issues• Comparison of two Smart Pointers
– Equality, Inequality, Ordering
• Checking and Error Reporting– Initialization checking – Checking before dereference
• const-ness– Smart Pointers to const and – const Smart Pointers
• Arrays• Multi-Threading / Locks
– Proxy Objects
04/12/23 7575
Smart Pointers in C++
Performance IssuesPerformance Issues
04/12/23 7676
Space Overhead in Smart Pointers
Ownership OverheadDestructive Copy Nil
Deep Copy (n-1)*sizeof(<Object>)
COW <= (n-1)*sizeof(<Object>)
RC: Non-Intrusive (n) 4pointer*n+4counter
RC: Non-Intrusive (1) 4pointer+4counter
RC: Intrusive 4counter
Reference Linking 4pointer_next*n+4pointer_prev*n
04/12/23 Source: http://www.boost.org/libs/smart_ptr/smarttests.htm 7777
Smart Pointer Timing (GCC, MSVC)
• Time in ns to acquire a pointer (default construction), perform n amortized copy operations on it & finally release it.
• The allocation time for the contained pointer is not included, but the time for it's deallocation is.
• A dumb pointer is a smart pointer that simply acquires and releases its contained pointer with no extra overhead. A raw pointer is a C pointer.
04/12/23 7878
Smart Pointers in Practice
A Few ExamplesA Few Examples
04/12/23 7979
Smart Pointers in Practice
• std::auto_ptr– Only Smart pointer in C++ standard library
– Uses a Destructive Copy
– Good for automatic (local) variables
– Cannot be used in STL• Not Copy Constructible
• Not Assignable
“Think of the Standard C++ auto_ptr as the Ford Escort of smart pointers:
A simple general-purpose smart pointer that doesn't have all the gizmos and luxuries of special-purpose or high-performance smart pointers, but that does many common things well and is perfectly suitable for regular daily use.” – C/C++ Users Jounral, October 1999.
04/12/23 Source: http://www.boost.org/libs/smart_ptr/smart_ptr.htm 8080
Smart Pointers in Practice
• Boost Library – shared_ptr– Well designed, Easy to use & Highly portable
– Single-threaded
– Shared Ownership• Reference Counted (non-intrusive)
– Has specializations• scoped_ptr – Simple sole ownership of single objects. Noncopyable.
• weak_ptr – Non-owning observers of an object owned by shared_ptr.
• intrusive_ptr – Shared ownership of objects with an embedded reference count.
• Array Versions
04/12/23 8181
Smart Pointers in Practice
• Component Object Model (COM)– Reference Counted Smart Pointer on Interfaces
• A pointer to an array of function pointers
– An implementation in C / C++ / VB …
– Structured Lifetime Management with user cooperation
– Addref() to increase reference count• Automatically called from QueryInterace() of the COM
– Release() to decrease count• COM is unloaded when reference count falls to zero
04/12/23 8282
std::auto_ptr
The Smart Pointer in Standard LibraryThe Smart Pointer in Standard Library
04/12/23 8383
std::auto_ptr
• The Philosophy
• Destructive Copy – A Review
• Standard Interface & Sample Implementation
• Using Idioms
• History
• Portability
• Pitfalls
04/12/23 8484
std::auto_ptr
• An auto_ptr is an object that acts just like a pointer, except it automatically deletes what it points to when it (the auto_ptr) is destroyed.
“Think of the Standard C++ auto_ptr as the Ford Escort of smart pointers:
A simple general-purpose smart pointer that doesn't have all the gizmos and luxuries of special-purpose or high-performance smart pointers, but that does many common things well and is perfectly suitable for regular daily use.” – C/C++ Users Jounral, October 1999.
04/12/23 8585
std::auto_ptr
• Storage Policy– Normal
• Ownership Policy– Destructive Copy
• Conversion Policy– Explicit for Raw Pointer
– Implicit for compatible types (Base, Derived)
– Implicit for auto_ptr reference
– Null Test – Not provided
• Checking Policy– None
04/12/23 8686
std::auto_ptr
• Good for automatic (local) variables• RAII – Resource Acquisition Is Initialization
idiom • Selectively disallows “unwanted” operations• Lifetime Management
– Automatically manages dynamically created objects– Exception Safe
• Cannot be used in STL– Not Copy Constructible– Not Assignable
04/12/23 8787
std::auto_ptr
Standard InterfaceStandard Interface
Sample ImplementationSample Implementation
04/12/23 8888
std::auto_ptr Interface
// auto_ptr class
template<class X> class auto_ptr {
private:
// A wrapper class for reference // A wrapper class for reference
// semantics// semantics with auto_ptrauto_ptr
template<class Y> struct auto_ptr_ref {};
// The raw pointer it holds
X* _ptr;
public:
// The type of the pointee
typedef X element_type;
04/12/23 8989
std::auto_ptr Interfaceexplicit auto_ptr(X* _p = 0) throw(); // Constructor
auto_ptr(auto_ptr& _a) throw(); // Copy Constructor
template<class Y> // Copy Compatible Typeauto_ptr(auto_ptr<Y>& _a) throw();
auto_ptr& operator=(auto_ptr& _a) throw() // Assignment
template<class Y> // Assignment Compatible Typeauto_ptr& operator=(auto_ptr<Y>& _a) throw();
~auto_ptr(); // Destructor
04/12/23 9090
std::auto_ptr Interface// De-Reference X& operator*() const throw();
// Indirection X* operator->() const throw();
// Expose Raw Pointer X* get() const throw();
// Relinquish Ownership X* release() throw();
// Reset Ownership void reset(X* _p = 0) throw();
04/12/23 9191
std::auto_ptr Interface
// Copy from Reference Type
auto_ptr(auto_ptr_ref<X> _ref) throw();
// Cast to Compatible Reference Type
template<class Y>
operator auto_ptr_ref<Y>() throw();
// Cast to Compatible Type
template<class Y>
operator auto_ptr<Y>() throw();
};
04/12/23 9292
std::auto_ptr Implementation
// De-Reference
X& operator*() const throw() { return *_ptr; }
// Indirection
X* operator->() const throw() { return _ptr; }
// Expose Raw Pointer
X* get() const throw() { return _ptr; }
04/12/23 9393
std::auto_ptr Implementation// Release Ownership X* release() throw() {
X* _tmp = _ptr;_ptr = 0;return _tmp;
}
// Reset Ownership void reset(X* _p = 0) throw() {
if (_p != _ptr) {delete _ptr;_ptr = _p;
}}
04/12/23 9494
std::auto_ptr Implementation// Constructor explicit auto_ptr(X* _p = 0) throw(): _ptr(_p) {}
// Copy Constructor auto_ptr(auto_ptr& _a) throw(): _ptr(_a.release()) {}
// Copy Constructor Compatible Type template<class Y> auto_ptr(auto_ptr<Y>& _a) throw():
_ptr(_a.release()) { }
// Assignment auto_ptr& operator=(auto_ptr& _a) throw() {
reset(_a.release());return *this;
}
04/12/23 9595
std::auto_ptr Implementation
// Assignment Compatible Type
template<class Y>
auto_ptr& operator=(auto_ptr<Y>& _a) throw() {
reset(_a.release());
return *this;
}
// Destructor
~auto_ptr() { delete _ptr; }
04/12/23 9696
std::auto_ptr Implementation// Convert an auto_ptr into and from an auto_ptr_ref
// automatically as needed.
// Copy from Reference
auto_ptr(auto_ptr_ref<X> _ref) throw():
_ptr(_ref._ptr) {}
// Cast to Reference Type
template<class Y> operator auto_ptr_ref<Y>() throw()
{ return auto_ptr_ref<Y>(this->release()); }
// Cast to Compatible Type
template<class Y> operator auto_ptr<Y>() throw()
{ return auto_ptr<Y>(this->release()); }
04/12/23 9797
std::auto_ptr
Using IdiomsUsing Idioms
04/12/23 9898
Using std::auto_ptr – Safety // Unsafe code
void f() {T* pt(new T);
/*...more code...*/
delete pt;
}
// Safe code, with auto_ptr void f() {
auto_ptr<T> pt(new T);
/*...more code...*/
}
// pt's destructor is called as it goes out of
// scope - the object is deleted automatically
04/12/23 9999
Using std::auto_ptr – const-ness
// Caveat: gets ownership of passed argument
template <class T>void dangerous_function(std::auto_ptr<T>);
// safe auto_ptr
const std::auto_ptr<int> p(new int);
// OK, change value to which p refers
*p = 42;
// COMPILE-TIME ERROR!
dangerous_function(p); Guard Maelstrom Effect
04/12/23 100100
Using std::auto_ptr – Common Stuff
void g() {
T* pt1 = new T; // Here, we own the object
auto_ptr<T> pt2(pt1); // Pass ownership
// use the auto_ptr we'd use a simple pointer
*pt2 = 12; // Same as "*pt1 = 12;“
pt2->SomeFunc(); // Same as "pt1->SomeFunc();"
assert(pt1 == pt2.get()); // Check pointer
T* pt3 = pt2.release(); // Take back ownership
delete pt3; // Delete the object ourselves
} // pt2 doesn't own any pointer, and so won't
// try to delete it... OK, no double delete
04/12/23 101101
Using std::auto_ptr – reset()
void h() {
auto_ptr<T> pt(new T(1));
…
// deletes the first T that was
// allocated with "new T(1)"
pt.reset(new T(2));
…
} // finally, pt goes out of scope and
// the second T is also deleted
04/12/23 102102
Using std::auto_ptr – Ownership
// Transferring ownership
void f() {
auto_ptr<T> pt1(new T);
auto_ptr<T> pt2; // A null pointer
pt1->DoSomething(); // OK
pt2 = pt1; // now pt2 owns the ptr,
// and pt1 does not
pt2->DoSomething(); // OK
pt1->DoSomething(); // error! a null pointer
} // as we go out of scope, pt2's destructor
// deletes the pointer, but pt1's does nothing
04/12/23 103103
Using std::auto_ptr – Source
• Source() allocates a new object and returns it to the caller in a completely safe way, by letting the caller assume ownership of the pointer.
• Even if the caller ignores the return value the allocated object will always be safely deleted.
auto_ptr<T> Source() { return auto_ptr<T>(new Y); }
// Source could be a Class Factory
auto_ptr<X> pt(Source()); // takes ownership
Resource creator
04/12/23 104104
Using std::auto_ptr – Sink
• Sink() takes an auto_ptr by value and therefore assumes ownership of it.
• When Sink() is done, the deletion is performed as the local auto_ptr object goes out of scope (as long as Sink() itself hasn't handed off ownership to someone else).
• The Sink() function below doesn't actually do anything with its parameter, so calling "Sink( pt );" is a fancy way of writing "pt.reset(0);", but normally a sink function would do some work with the object before freeing it.
void Sink(auto_ptr<T> pt) {}
Resource releaser
04/12/23 105105
std::auto_ptr
HistoryHistory
04/12/23 106106
History of auto_ptr
• Version 1: – Prior to March 1996. – Referred to as the CD-1 version, after the ISO draft.
• Version 2:– From March 1996 until November 1997. – Referred to as the CD-2 version.
• Version 3:– From November 1997. – Made to the final ISO/ANSI standard for C++. – Also known as the FDIS ("Final Draft International
Standard") version.
04/12/23 107107
History of auto_ptr• Version 1:
– Only one auto_ptr is allowed to point to an object. – Copying an auto_ptr sets its dumb pointer to null.
• Version 2:– Multiple auto_ptrs may point to an object, but exactly one is
designated the "owner". – When the owning auto_ptr is destroyed, it deletes what it points to. – When non-owning auto_ptrs are destroyed, nothing happens to the
object they point to. – Copying an auto_ptr transfers ownership.
• Version 3:– Same as Version 1, but the interface was modified to prevent
behavioral anomalies present in Version 1.
04/12/23 108108
auto_ptr: Version 1template<class T>
class auto_ptr { public: explicit auto_ptr(T *p = 0); template<class U> auto_ptr(auto_ptr<U>& rhs); ~auto_ptr(); template<class U> auto_ptr<T>&
operator=(auto_ptr<U>& rhs); T& operator*() const; T* operator->() const; T* get() const; T* release(); void reset(T *p = 0); private: T *pointee;
}; Note:
• No const on copy parameters
• No throw();
04/12/23 109109
auto_ptr: Version 1 – ShortcomingsPrevents any use of an auto_ptr returned from a function:auto_ptr f(); // f returns an auto_ptr
auto_ptr p1(f()); // Error! can't copy an auto_ptr // returned from a function
auto_ptr p2; p2 = f(); // Error! can't use an auto_ptr// returned from a function
as// the source of an
assignment
Why?– Objects returned from functions are temporary objects, and temporary
objects can't be bound to reference parameters unless the parameters are references to const objects.
– auto_ptr's copy constructor and assignment operator take reference-to-non-const parameters
04/12/23 110110
auto_ptr: Version 2template<class X>
class auto_ptr { mutable bool owner; X* px; template<class Y> friend class auto_ptr;
public: explicit auto_ptr(X* p=0) ;template<class Y> auto_ptr(const auto_ptr<Y>& r);template<class Y> auto_ptr&
operator=(const auto_ptr<Y>& r) ~auto_ptr();X& operator*() const; X* operator->() const; X* get() const; X* release() const;
}
• const introduced in Copy• reset() dropped
• A bool member to track ownership
04/12/23 111111
auto_ptr: Version 2template<class Y> auto_ptr(const auto_ptr<Y>& r) :
owner(r.owner), px(r.release()) {}
template<class Y> auto_ptr& operator=(const auto_ptr<Y>& r) {
if ((void*)&r != (void*)this) { if (owner) delete px; owner = r.owner; px = r.release();
} return *this;
} ~auto_ptr() { if (owner) delete px; }X* release() const { owner = 0; return px; } };
These methods matter!
04/12/23 112112
auto_ptr: Version 2 – Shortcomings• Destructive Copy Semantics lost
– Unique ownership retained
– Double Deletion Error protected
• Potential Dangling Pointer• Not safe for const auto_ptr
– Maelstrom Effect returns
04/12/23 113113
auto_ptr: Version 3namespace std { template<class X> class auto_ptr {
template<class Y> struct auto_ptr_ref {}; public:
typedef X element_type; explicit auto_ptr(X* p=0) throw();auto_ptr(auto_ptr&) throw(); template<class Y> auto_ptr(auto_ptr<Y>&) throw();auto_ptr& operator=(auto_ptr&) throw()template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw(); ~auto_ptr() throw(); X& operator*() const throw(); X* operator->() const throw(); X* get() const throw(); X* release() throw(); void reset(X* p=0) throw(); auto_ptr(auto_ptr_ref<X>) throw(); template<class Y> operator auto_ptr_ref<Y>() throw(); template<class Y> operator auto_ptr<Y>() throw(); }; }
Colvin-Gibbons Trick
04/12/23 114114
auto_ptr: Version 3 – Advantages • Clean handling of function return value
– Direct Initialization: Same Typeauto_ptr<int> source();
auto_ptr<int> p(source());
// Constructor: auto_ptr<int>::auto_ptr(auto_ptr_ref<int>);
// Conversion:
auto_ptr<int>::operator auto_ptr_ref<int>();
04/12/23 115115
auto_ptr: Version 3 – Advantages • Clean handling of function return value
– Copy Initialization: Same Type auto_ptr<int> source();
void sink(auto_ptr<int>);
main() { sink(source()); }
// Constructor: auto_ptr<int>::auto_ptr(auto_ptr_ref<int>);
// Conversion:
auto_ptr<int>::operator auto_ptr_ref<int>();
04/12/23 116116
auto_ptr: Version 3 – Advantages• Clean handling of function return value
– Direct Initialization: Base-from-Derived Typestruct Base{};
struct Derived : Base{};
auto_ptr<Derived> source();
auto_ptr<Base> p(source());
// Constructor: auto_ptr<Base>::auto_ptr(auto_ptr_ref<Base>);
// Conversion:
auto_ptr<Derived>::operator auto_ptr_ref<Base>();
04/12/23 117117
auto_ptr: Version 3 – Advantages• Clean handling of function return value
– Copy Initialization: Base-from-Derived Typestruct Base {};
struct Derived : Base {};
auto_ptr<Derived> source();
void sink(auto_ptr<Base>);
main() { sink(source()); }
// Constructor: auto_ptr<Base>::auto_ptr<Derived>(auto_ptr<Derived> &);
// Conversion:
auto_ptr<Derived>::operator auto_ptr<Base>();
04/12/23 118118
auto_ptr: Version 3 – Advantages• Complete Destructive Copy Semantics
04/12/23 119119
std::auto_ptr
PortabilityPortability
04/12/23 120120
What is your auto_ptr?• Check with the compiler:
– Look for <memory> in standard library
• Sample Variations– GCC 3.4 & VC++ 7.1 implements Version 3
– VC++ 6.0 implements Version 2
• Member Function Template– Support is still limited between compilers
– Customizes auto_ptr to eliminate member function template
– Sample: VC++ 6.0
• Consider having your own auto_ptr
04/12/23 121121
std::auto_ptr
PitfallsPitfalls
04/12/23 122122
auto_arrays!• Do not use auto_ptr with arrays:#include <iostream>#include <memory>#include <string>using namespace std;int c = 0;class X {public:
X(): s("1234567890") { ++c; }~X() { --c; }string s;
};template<typename T> void f(size_t n) {
{auto_ptr<T> p1(new T);auto_ptr<T> p2(new T[n]);
}cout << c << " ";// report # of X objects that currently exist
}void main() { while (true) { f<X>(100); } }
04/12/23 123123
auto_arrays! Survival• Always match new – delete and new[] –
delete[].• Use vector<T> in STL
04/12/23 124124
Coping with COAP• Never create containers of auto_ptr’s (COAP):
– STL Containers need an FCO copy semantics – auto_ptr does not support that
– Suppose sort() below is implemented as quicksort. The moment the “pivot element” is copied in a temporary it is lost from seqs!
bool CompareSequenceAP(const auto_ptr<Sequence>& lhs, const auto_ptr<Sequence>& rhs)
{ return *lhs < *rhs; } // operator< exists in Sequencevector<auto_ptr<Sequence> > seqs;sort(seqs.begin(), seqs.end(), CompareSequenceAP);
– C++ Standard prohibits such situations in compilation (Recall, auto_ptr copies take reference-to-non-const).
04/12/23 125125
Coping with COAP: Survival• Use other (non-destructive copy) Smart Pointers
with STL Containers
04/12/23 126126
Smart Pointers in C++
References & CreditsReferences & Credits
04/12/23 127127
References: Books• Effective C++ by Scott Meyers
• More Effective C++: 35 New Ways to Improve Your Programs and
Designs – Scott Meyers, Pearson Education & AWP 1999
• Modern C++ Design: Generic Programming & Design Pattern Applied – Andrei Alexandrescu, Pearson Education 2001
• C++ Templates: The Complete Guide – David Vandevoorde & Nicolai M. Josuttis, Pearson Education & AWP 2003
• Exceptional C++ by Herb Sutter
• More Exceptional C++ by Herb Sutter
• The C++ Programming Language by Bjarne Stroustrup
04/12/23 128128
References: Papers
• A static analyzer for finding dynamic programming errors - William R. Bush, Jonathan D. Pincus and David J. Sielaff, Software Practice Experience 2000; 30:775–802
– Used for PREfix simulation results on Pointer Hazards
04/12/23 129129
References: Online Help
• MSVC 6.0• MSVC 7.1• GCC
04/12/23 130130
References: Codes
• <memory> header of Your Compiler
04/12/23 131131
References: Code URLs• CodeProject – http://www.codeproject.com
– Smart Pointers• A Simple Smart Pointer• A Smart Pointer Capable of Object Level Thread• Smart Pointers to boost your code• The Fastest Smart Pointer in the West• The Safest Smart Pointer of the East• A generic C++ template class to implement a Smart Pointer• A COM Smart Pointer
– Pointers (General)• How to do pointers in VB? • Pointers in Visual Basic using Undocumented Functions
– Garbage Collectors• A garbage collection framework for C++• A garbage collection framework for C++ - Part II• Synchronization and Reference Counting Garbage Collection
• Boost Library – http://www.boost.org– shared_ptr and its variants– Smart Pointer Timing
04/12/23 132132
References: Knowledge URLs• “Informal Language Comparison Chart(s)” – www.smallscript.org/Language%20Comparison%20Chart.asp • “Pointer Basics” – http://cslibrary.stanford.edu/106/ • “Pointers, References & Values” – www.goingware.com • “Using auto_ptr Effectively” – www.cuj.com, October 1999 and
www.gotw.ca/publications/using_auto_ptr_effectively.htm• “Smart pointer templates in C++” by David Harvey – www.davethehat.com/articles/smartp.htm • “C++ Without Memory Errors” by Dejan Jelović –
www.jelovic.com/articles/cpp_without_memory_errors_slides.htm • “Smart Pointer Thread Safety” by Dejan Jelović – www.jelovic.com/articles/smart_pointer_thread_safety.htm • “The New C++: Smart(er) Pointers” by Herb Sutter – www.cuj.com, August 2000.• “Programming Language Comparion” by Jason Voegele – www.jvoegele.com/software/langcomp.html • “The case against C” by P. J. Moylan – www.modulaware.com/mdlt35.htm • “Smart Pointers - What, Why, Which?” by Yonat Sharon – http://ootips.org/yonat/4dev/smart-pointers.html • RAII Idiom and Managed C++
http://www.codeproject.com/managedcpp/managedraii.asp • GNU GCC 3.4
http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/memory-source.htmlhttp://www.cs.huji.ac.il/~etsman/Docs/gcc-3.4-base/libstdc++/html_user/debug_8h-source.html
• MSDN Libraryhttp://msdn.microsoft.com
04/12/23 133133
Credits / Acknowledgements
• Reena – helped in desk editing this presentation.
• Scott Meyers – helped by providing pointers to various smart
pointer resources.
• Shyamal– for being a patient audience to my early
stages of development of understanding
04/12/23 134134
Thank You
Don’t Beware of Don’t Beware of Pointers – Pointers –
Just Be Aware of Just Be Aware of Smart PointersSmart Pointers
04/12/23 135135
std::tr1::shared_ptr std::tr1::weak_ptr
Additional Smart Pointers in TR1 Additional Smart Pointers in TR1 Standard Library of C++Standard Library of C++
ISO/IEC PDTR 19768. Doc No: N1745=05-0005. Date: 2005-01-17
04/12/23 136136
Motivation for a Shared-Ownership Smart Pointer
• Is a reasonable default choice for many (or even most) smart pointer needs
– consistent with the spirit of the Standard Library. • Can be used in Standard Library containers
– filling a major embarrassing gap in the current Standard Library.
• Has been reinvented thousands of times • Is very hard to specify and implement
correctly, particularly as regards – exception safety, – thread safety, and – weak pointers.
04/12/23 137137
Motivation for the shared_ptr (Shared-Ownership)
• Has the same object type regardless of features used– greatly facilitating interoperability between libraries, including
third-party libraries. • Supports custom deallocators
– allowing user-specified memory policies and extending management to a wide range of object types, such as COM objects.
• Supports weak pointers– allowing cycle breaking and supporting observer applications such
as caches. • Delivers exceptional expressive power without additional
template parameterization– easing use by novices and experts alike.
• Supports encapsulation and implementation hiding via – the ability to safely use incomplete classes, – protected non-virtual base destructors, and– the ability to hide allocation details.
04/12/23 138138
Motivation for the shared_ptr (Shared-Ownership)
• "Just works" in numerous tricky situations such as – calling the right destructor and crossing load-unit/heap
boundaries. • Requires no language support. • Does not prevent users or the Standard Library itself from
later adding other smart pointers – to meet additional needs.
• Allows several implementation techniques– leaving implementers room for tradeoffs.
• Reflects existing practice with a decade of refinement and use– including many non-obvious enhancements.
• Is widely recommended– in books, magazine articles, and newsgroup postings – by both C++ experts and everyday users.
04/12/23 139139
Motivation for the weak_ptr (Shared-Ownership-Observer)
• Supports data structures with cyclic pointers– either weak_ptr or shared_ptr covers all important shared-
ownership use cases.
• Supports observer idioms such as caches. – Allows functions or classes to keep references to objects
without affecting their lifetime.
• Replaces unsafe uses of raw pointers– eliminating the possibility of following a dangling pointer.
• Is very hard to specify and implement correctly. • Reflects existing practice with a decade of
refinement and use.
04/12/23 140140
Implementation Difficulty of Shared-Ownership Smart Pointer
• Example 1:
shared_ptr<X> p(new X);
• It is very common for even expert implementations to leak the X object when the smart pointer constructor throws an exception.
04/12/23 141141
Implementation Difficulty of Shared-Ownership Smart Pointer
• Example 2:
p.reset(new X);
• The behavior when reset throws should be to delete the X object, and have no other effects, i.e. p must be left unchanged.
04/12/23 142142
Implementation Difficulty of Shared-Ownership Smart Pointer
• Example 3: (Boost's shared_ptr_test.cpp)
struct X { boost::shared_ptr<X> next; }; void test() {
boost::shared_ptr<X> p(new X); p->next = boost::shared_ptr<X>(new X); p = p->next; BOOST_TEST(!p->next);
}
• Surprisingly many experts get this wrong.
04/12/23 143143
Additional Implementation Difficulty of weak_ptr
• The stored pointer in a weak_ptr can be – invalidated literally at any time; even in strictly
single threaded and synchronous programs it is possible for the code to call a library that calls back a routine invalidating a weak_ptr.
• The weak_ptr implementation must take care not to access the stored pointer value after the weak_ptr has expired.
– Note: The value of a pointer referring to deallocated storage is indeterminate.
• weak_ptr interface should protect users from– accidentally accessing a pointer to deallocated
storage.
04/12/23 144144
Impact on the Standard
• A pure library extension. • Changes to an existing header, <memory>,
– does not require changes to any standard classes or functions and
– does not require changes to any of the standard requirement tables.
• Does not require any changes in the core language, and is implemented in standard C++.
• Does not depend on any other library extensions.
04/12/23 145145
Design Decisions
A. General Principles
B. shared_ptr
C. weak_ptr
D. enable_shared_from_this
E. Size and Performance
F. Alternatives
04/12/23 146146
Design Decisions: General Principles
1. "As Close to Raw Pointers as Possible, but no Closer"
2. "Just Do the Right Thing"
3. No Extra Parameters
4. The "Shares Ownership with" Equivalence Relation
5. The "Empty Pointer" Concept
04/12/23 147147
As Close to Raw Pointers as Possible’ but no close
• Interesting Properties of Raw Pointers:– Copy – Assign – Pass by value– Return by value– Destroy a pointer to an incomplete type. – Refer to any object's address using a pointer to void. – Pointers to derived classes are implicitly convertible to
pointers to base classes– Use static_cast or dynamic_cast to perform the opposite
conversion.• The two main problems are:
– The need for manual resource management and – The possibility of accessing an invalid (dangling) pointer.
04/12/23 148148
As Close to Raw Pointers as Possible’ but no Closer
• shared_ptr and weak_ptr fully support – incomplete types, – cv-qualified types, – void as a template parameter, and– derived-to-base implicit conversions.
• shared_ptr supports – static cast,– dynamic casts, and – will automatically delete (or otherwise release) the object
it owns when the last shared_ptr instance is destroyed.
• weak_ptr eliminates – the possibility of accessing a dangling pointer.
04/12/23 149149
Just Do the Right Thing
• Consider the scenario:– A shared_ptr can be created in a shared library and then – destroyed by the application. – By using implicit conversions, the last shared_ptr instance
to an object may have a template parameter • void, • a base with an inaccessible or non-virtual destructor, or • an incomplete type.
• What should happen during destructions?– The implementation should invoke the proper destructor
or operator delete. • The information necessary to properly destroy the
managed object is fully captured at the point where the smart pointer is constructed.
04/12/23 150150
No Extra Parameter
• The proposed smart pointers have a single template parameter, the type of the pointee.
• Avoid additional parameters to ensure – interoperability between libraries from
different authors, and – makes shared_ptr easier to use, teach and
recommend.
04/12/23 151151
“Shares Ownership With” is an Equivalence Relation
• "p shares ownership with q" to indicate that – p and q originated from the same smart pointer and– p and q own the same object.
• "Shares ownership with" is an equivalence relation.
– reflexive • p shares ownership with p
– commutative • if p shares ownership with q, q also shares ownership
with p– transitive
• if p shares ownership with q and q shares ownership with r, p shares ownership with r.
04/12/23 152152
“Shares Ownership With” is an Equivalence Relation
• This relation divides all non-empty smart pointers into equivalence classes.
• The number of shared_ptr instances in p's equivalence class can be obtained using p.use_count().
• The last shared_ptr instance in a given equivalence class is responsible for destroying the owned object.
• Implementations– Counted:
• Two smart pointers share ownership when they share the same reference count.
– Linked List:• Two smart pointers share ownership when they are part of the
same list.
04/12/23 153153
The “Empty Pointer” Concept
• A smart pointer is called empty if it is – It is Default-constructed, or if– Its instances that have been reset()
• Needed for– nothrow guarantee for
• shared_ptr::shared_ptr(), • shared_ptr::reset(), • weak_ptr::weak_ptr(), and • weak_ptr::reset().
– p.reset() used as delete p for raw pointers• need guarantee that this operation doesn't throw.
04/12/23 154154
The “Empty Pointer” Concept
• Strategy to guarantee nothrow – default constructors and reset() allowed to
skip the allocation for tracking ownership– produce empty smart pointers that do not
track ownership.
04/12/23 155155
The “Empty Pointer” Concept
• Implementation Strategy 1 – Reuse one or more statically allocated
reference counts for default-constructed pointer instances.
– empty pointers, then, represent one or more equivalence classes under the ownership equivalence relation
– use_count() can return "realistic" values that increase and decrease as additional empty pointers are created or destroyed.
04/12/23 156156
The “Empty Pointer” Concept
• Implementation Strategy 2 (Boost)– Do not allocate a reference count at all for
empty pointers– Use a NULL pointer to count as an "empty
mark". – empty pointers have a constant use_count()
• zero in Boost
– empty pointers returning zero from use_count(), eliminates nearly all of the unspecified behavior.
04/12/23 157157
The “Empty Pointer” Concept
• Considerint main() {
try { shared_ptr<int> sp0; weak_ptr<int> wp(sp0); shared_ptr<int> sp(wp); cout << "Done\n";
} catch (exception const & e) { cerr << e.what() << '\n'; }
}
• Strategy #1 prints “Done”• Strategy #2 throws std::tr1::bad_weak_ptr
04/12/23 158158
Design Decisions: shared_ptr
1. Pointer Constructor2. Deleter Constructor3. weak_ptr Constructor4. std::auto_ptr Constructor5. Assignment and reset6. operator->7. use_count and unique8. Conversion Operator9. operator<10. operator<<11. Casts12. get_deleter
04/12/23 159159
Design Decisions: weak_ptr
1. Default Constructor
2. Converting Constructor
3. lock
04/12/23 160160
Design Decisions: enable_shared_from_this
04/12/23 161161
Design Decisions: Size & Performance
1. Memory Footprint
2. Construction Performance
3. Copy Performance
04/12/23 162162
Design Decisions: Alternatives
1. Policy Based Smart Pointers
2. Garbage Collection
04/12/23 163163
Incomplete Type• An incomplete type is a type that describes an identifier but
lacks information needed to determine the size of the identifier.
• The following are incomplete types: – Type void – Array of unknown size – Arrays of elements that are of incomplete type – Structure, union, or enumerations that have no definition – Pointers to class types that are declared but not defined – Classes that are declared but not defined
• void is an incomplete type that cannot be completed. • Incomplete types must be completed before being used to
declare an object.• A pointer to an incomplete type can be defined.
04/12/23 164164
Smart Pointer Classes in <memory>
• In namespace std – template<class T> class auto_ptr;– As already in the Standard
• In namespace std::tr1– class bad_weak_ptr;– template<class T> class shared_ptr;– template<class T> class weak_ptr;– These are propose additions
04/12/23 165165
class bad_weak_ptrnamespace std {
namespace tr1 {class bad_weak_ptr: public std::exception {
public:
bad_weak_ptr();
};
} // namespace tr1
} // namespace std• An exception of type bad_weak_ptr is thrown by the shared_ptr
constructor taking a weak_ptr.
• Postconditions: what() returns "tr1::bad_weak_ptr".
• Throws: nothing.
04/12/23 166166
class shared_ptrnamespace std {
namespace tr1 {
template<class T> class shared_ptr {
public:
typedef T element_type;
// [2.2.3.1] constructors
shared_ptr();
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
shared_ptr(shared_ptr const& r);
template<class Y> shared_ptr(shared_ptr<Y> const& r);
template<class Y> explicit
shared_ptr(weak_ptr<Y> const& r);
template<class Y> explicit shared_ptr(auto_ptr<Y>& r);
04/12/23 167167
class shared_ptr// [2.2.3.2] destructor~shared_ptr();
// [2.2.3.3] assignmentshared_ptr& operator=(shared_ptr const& r);template<class Y> shared_ptr& operator=
(shared_ptr<Y> const& r);template<class Y> shared_ptr& operator=
(auto_ptr<Y>& r);
// [2.2.3.4] modifiersvoid swap(shared_ptr& r);void reset();template<class Y> void reset(Y * p);template<class Y, class D> void reset(Y * p, D d);
04/12/23 168168
class shared_ptr// [2.2.3.5] observersT* get() const;T& operator*() const;T* operator->() const;long use_count() const;bool unique() const;operator unspecified-bool-type() const;}; // template<class T> class shared_ptr
04/12/23 169169
class shared_ptr// [2.2.3.6] shared_ptr comparisonstemplate<class T, class U> bool operator==
(shared_ptr<T> const& a, shared_ptr<U> const& b);template<class T, class U> bool operator!=
(shared_ptr<T> const& a, shared_ptr<U> const& b);template<class T, class U> bool operator<
(shared_ptr<T> const& a, shared_ptr<U> const& b);
// [2.2.3.7] shared_ptr I/Otemplate<class E, class T, class Y>basic_ostream<E, T>& operator<< (basic_ostream<E, T>& os,
shared_ptr<Y> const& p);
// [2.2.3.8] shared_ptr specialized algorithmstemplate<class T> void swap(shared_ptr<T>& a,
shared_ptr<T>& b);
04/12/23 170170
class shared_ptr// [2.2.3.9] shared_ptr caststemplate<class T, class U> shared_ptr<T>
static_pointer_cast(shared_ptr<U> const& r);template<class T, class U> shared_ptr<T>
dynamic_pointer_cast(shared_ptr<U> const& r);template<class T, class U> shared_ptr<T>
const_pointer_cast(shared_ptr<U> const& r);
// [2.2.3.10] shared_ptr get_deletertemplate<class D, class T> D *
get_deleter(shared_ptr<T> const& p);} // namespace tr1} // namespace std
04/12/23 171171
Design Decisions• A. General Principles
– 1. "As Close to Raw Pointers as Possible, but no Closer"– 2. "Just Do the Right Thing"– 3. No Extra Parameters– 4. The "Shares Ownership with" Equivalence Relation– 5. The "Empty Pointer" Concept
• B. shared_ptr– 1. Pointer Constructor– 2. Deleter Constructor– 3. weak_ptr Constructor– 4. std::auto_ptr Constructor– 5. Assignment and reset– 6. operator->– 7. use_count and unique– 8. Conversion Operator– 9. operator<– 10. operator<<– 11. Casts– 12. get_deleter
• C. weak_ptr– 1. Default Constructor– 2. Converting Constructor– 3. lock
• D. enable_shared_from_this• E. Size and Performance
– 1. Memory Footprint– 2. Construction Performance– 3. Copy Performance
• F. Alternatives– 1. Policy Based Smart Pointers– 2. Garbage Collection