Beware of Pointers

171
October 28, November 4, December 2, 2005 Beware of Pointers! Dr. Partha Pratim Das Interra Systems (India) Pvt. Ltd. Resource Management Series

description

I talk on a variety of Smart Pointers in this lecture as Memory Management idioms in C++.

Transcript of Beware of Pointers

Page 1: Beware of Pointers

October 28, November 4,

December 2, 2005

Beware of Pointers!

Dr. Partha Pratim DasInterra Systems (India) Pvt. Ltd.

Resource Management Series

Page 2: Beware of Pointers

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

Page 3: Beware of Pointers

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

Page 4: Beware of Pointers

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

Page 5: Beware of Pointers

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”

Page 6: Beware of Pointers

04/12/23 66

Agenda

• std::auto_ptr – The Philosophy– Standard Interface & Sample Implementation– Using Idioms– History– Portability– Pitfalls

• References & Credits

Page 7: Beware of Pointers

04/12/23 77

Raw Pointers

A Raw Deal?A Raw Deal?

Page 8: Beware of Pointers

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++

Page 9: Beware of Pointers

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[]

Page 10: Beware of Pointers

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

Page 11: Beware of Pointers

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;

Page 12: Beware of Pointers

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;

Page 13: Beware of Pointers

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;

}

Page 14: Beware of Pointers

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;

}

Page 15: Beware of Pointers

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;

}

Page 16: Beware of Pointers

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

Page 17: Beware of Pointers

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

Page 18: Beware of Pointers

04/12/23 1818

A Pointer-free World

Reality or Utopia?Reality or Utopia?

Page 19: Beware of Pointers

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

Page 20: Beware of Pointers

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

Page 21: Beware of Pointers

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

Page 22: Beware of Pointers

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)

Page 23: Beware of Pointers

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)

Page 24: Beware of Pointers

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

• …

Page 25: Beware of Pointers

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

Page 26: Beware of Pointers

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

Page 27: Beware of Pointers

04/12/23 2727

Smart Pointers in C++

The Smartness …The Smartness …

Page 28: Beware of Pointers

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

Page 29: Beware of Pointers

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

Page 30: Beware of Pointers

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

};

Page 31: Beware of Pointers

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();

Page 32: Beware of Pointers

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);

Page 33: Beware of Pointers

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?

Page 34: Beware of Pointers

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

};

Page 35: Beware of Pointers

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();

};

Page 36: Beware of Pointers

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

Page 37: Beware of Pointers

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.

Page 38: Beware of Pointers

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.

Page 39: Beware of Pointers

04/12/23 3939

Smart Pointers in C++

Storage PolicyStorage Policy

Page 40: Beware of Pointers

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*

Page 41: Beware of Pointers

04/12/23 4141

Smart Pointers in C++

Ownership Management PolicyOwnership Management Policy

Page 42: Beware of Pointers

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

Page 43: Beware of Pointers

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=

Page 44: Beware of Pointers

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

} ...

};

Page 45: Beware of Pointers

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.

Page 46: Beware of Pointers

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.

Page 47: Beware of Pointers

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=

Page 48: Beware of Pointers

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_))

{ ... }

...

};

Page 49: Beware of Pointers

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);

}

};

Page 50: Beware of Pointers

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.

Page 51: Beware of Pointers

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.

Page 52: Beware of Pointers

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=

Page 53: Beware of Pointers

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

Page 54: Beware of Pointers

04/12/23 5454

Ownership Policy: Reference Counting: Non-Intrusive Counter

• Additional count pointer removed.• But additional access level means slower speed.

Page 55: Beware of Pointers

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

Page 56: Beware of Pointers

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.

Page 57: Beware of Pointers

04/12/23 5757

Ownership Policy: Reference Linking

• Overhead of 2 additional pointers

• Doubly-linked list for constant time:

– Append,

– Remove & Empty detection.

Page 58: Beware of Pointers

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.

Page 59: Beware of Pointers

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”?

Page 60: Beware of Pointers

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.

Page 61: Beware of Pointers

04/12/23 6161

Smart Pointers in C++

Implicit ConversionImplicit Conversion

Page 62: Beware of Pointers

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_; } ...

};

Page 63: Beware of Pointers

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

Page 64: Beware of Pointers

04/12/23 6464

Smart Pointers in C++

Null TestsNull Tests

Page 65: Beware of Pointers

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 ...

Page 66: Beware of Pointers

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

Page 67: Beware of Pointers

04/12/23 6767

Smart Pointers in C++

Checking PolicyChecking Policy

Page 68: Beware of Pointers

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

Page 69: Beware of Pointers

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();

} ...

};

Page 70: Beware of Pointers

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 */ }

Page 71: Beware of Pointers

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_; }

...

};

Page 72: Beware of Pointers

04/12/23 7272

Smart Pointers in C++

Other Design IssuesOther Design Issues

Page 73: Beware of Pointers

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[]

Page 74: Beware of Pointers

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

Page 75: Beware of Pointers

04/12/23 7575

Smart Pointers in C++

Performance IssuesPerformance Issues

Page 76: Beware of Pointers

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

Page 77: Beware of Pointers

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.

Page 78: Beware of Pointers

04/12/23 7878

Smart Pointers in Practice

A Few ExamplesA Few Examples

Page 79: Beware of Pointers

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.

Page 80: Beware of Pointers

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

Page 81: Beware of Pointers

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

Page 82: Beware of Pointers

04/12/23 8282

std::auto_ptr

The Smart Pointer in Standard LibraryThe Smart Pointer in Standard Library

Page 83: Beware of Pointers

04/12/23 8383

std::auto_ptr

• The Philosophy

• Destructive Copy – A Review

• Standard Interface & Sample Implementation

• Using Idioms

• History

• Portability

• Pitfalls

Page 84: Beware of Pointers

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.

Page 85: Beware of Pointers

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

Page 86: Beware of Pointers

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

Page 87: Beware of Pointers

04/12/23 8787

std::auto_ptr

Standard InterfaceStandard Interface

Sample ImplementationSample Implementation

Page 88: Beware of Pointers

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;

Page 89: Beware of Pointers

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

Page 90: Beware of Pointers

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();

Page 91: Beware of Pointers

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();

};

Page 92: Beware of Pointers

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; }

Page 93: Beware of Pointers

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;

}}

Page 94: Beware of Pointers

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;

}

Page 95: Beware of Pointers

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; }

Page 96: Beware of Pointers

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()); }

Page 97: Beware of Pointers

04/12/23 9797

std::auto_ptr

Using IdiomsUsing Idioms

Page 98: Beware of Pointers

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

Page 99: Beware of Pointers

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

Page 100: Beware of Pointers

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

Page 101: Beware of Pointers

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

Page 102: Beware of Pointers

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

Page 103: Beware of Pointers

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

Page 104: Beware of Pointers

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

Page 105: Beware of Pointers

04/12/23 105105

std::auto_ptr

HistoryHistory

Page 106: Beware of Pointers

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.

Page 107: Beware of Pointers

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.

Page 108: Beware of Pointers

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();

Page 109: Beware of Pointers

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

Page 110: Beware of Pointers

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

Page 111: Beware of Pointers

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!

Page 112: Beware of Pointers

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

Page 113: Beware of Pointers

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

Page 114: Beware of Pointers

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>();

Page 115: Beware of Pointers

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>();

Page 116: Beware of Pointers

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>();

Page 117: Beware of Pointers

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>();

Page 118: Beware of Pointers

04/12/23 118118

auto_ptr: Version 3 – Advantages• Complete Destructive Copy Semantics

Page 119: Beware of Pointers

04/12/23 119119

std::auto_ptr

PortabilityPortability

Page 120: Beware of Pointers

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

Page 121: Beware of Pointers

04/12/23 121121

std::auto_ptr

PitfallsPitfalls

Page 122: Beware of Pointers

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); } }

Page 123: Beware of Pointers

04/12/23 123123

auto_arrays! Survival• Always match new – delete and new[] –

delete[].• Use vector<T> in STL

Page 124: Beware of Pointers

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).

Page 125: Beware of Pointers

04/12/23 125125

Coping with COAP: Survival• Use other (non-destructive copy) Smart Pointers

with STL Containers

Page 126: Beware of Pointers

04/12/23 126126

Smart Pointers in C++

References & CreditsReferences & Credits

Page 127: Beware of Pointers

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

Page 128: Beware of Pointers

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

Page 129: Beware of Pointers

04/12/23 129129

References: Online Help

• MSVC 6.0• MSVC 7.1• GCC

Page 130: Beware of Pointers

04/12/23 130130

References: Codes

• <memory> header of Your Compiler

Page 131: Beware of Pointers

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

Page 132: Beware of Pointers

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

Page 133: Beware of Pointers

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

Page 134: Beware of Pointers

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

Page 135: Beware of 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

Page 136: Beware of Pointers

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.

Page 137: Beware of 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.

Page 138: Beware of Pointers

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.

Page 139: Beware of Pointers

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.

Page 140: Beware of Pointers

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.

Page 141: Beware of Pointers

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.

Page 142: Beware of Pointers

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.

Page 143: Beware of Pointers

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.

Page 144: Beware of Pointers

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.

Page 145: Beware of Pointers

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

Page 146: Beware of Pointers

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

Page 147: Beware of Pointers

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.

Page 148: Beware of Pointers

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.

Page 149: Beware of Pointers

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.

Page 150: Beware of Pointers

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.

Page 151: Beware of Pointers

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.

Page 152: Beware of Pointers

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.

Page 153: Beware of Pointers

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.

Page 154: Beware of Pointers

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.

Page 155: Beware of Pointers

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.

Page 156: Beware of Pointers

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.

Page 157: Beware of Pointers

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

Page 158: Beware of Pointers

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

Page 159: Beware of Pointers

04/12/23 159159

Design Decisions: weak_ptr

1. Default Constructor

2. Converting Constructor

3. lock

Page 160: Beware of Pointers

04/12/23 160160

Design Decisions: enable_shared_from_this

Page 161: Beware of Pointers

04/12/23 161161

Design Decisions: Size & Performance

1. Memory Footprint

2. Construction Performance

3. Copy Performance

Page 162: Beware of Pointers

04/12/23 162162

Design Decisions: Alternatives

1. Policy Based Smart Pointers

2. Garbage Collection

Page 163: Beware of Pointers

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.

Page 164: Beware of Pointers

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

Page 165: Beware of Pointers

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.

Page 166: Beware of Pointers

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);

Page 167: Beware of Pointers

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);

Page 168: Beware of Pointers

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

Page 169: Beware of Pointers

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);

Page 170: Beware of Pointers

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

Page 171: Beware of Pointers

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