Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington...

26
Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis

Transcript of Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington...

Page 1: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Writing Correct C++ Programs without

“delete”

Huang-Ming HuangCSE332 Guest Lecture

Washington University in St. Louis

Page 2: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Computer Architecture

int main() { int i = 0; Card c1; ….

return 0;}

Page 3: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Memory Model

Code

Stack

Heap

int main() { int i = 0; Card c1; ….

return 0;}

Global

Page 4: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

int func() { int i = 1; int j = 2; const char* str=“test”; Card a(“3C”); Card b=a; ….

return 0;}

Card::Card(const char* s) { ….

}

Program Execution Model

Code SectionStack Section

i

j

2

a.rank

a.suit

3

str

1CLUBS0

Card::Card(): rank(NO_RANK), suit(NO_SUIT) {}

b.rank NO_RANKNO_SUIT

3CLUBSb.suit

Card b; b=a;

Card& Card::operator= (const Card& other) {

rank= other.rank;

suit = other.suit;

}

Card::~Card() {}

0x12345678

Page 5: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Program Execution Model with Heap Allocation

Game* makeGame(const char* name) { Game* result = 0; if (strcmp(name,”bridge”)==0) result= new BridgeGame; else if (strcmp(name,”hearts”)==0) result= new HeartsGame; return result;}

CodeStackHeap

0x4518ab31

hearts

name

0result

0x34567890

0x4518ab31

int strcmp(const char* s1, const char* s2){ …. …. return result;}10

1. Find an empty space in heap for HeartsGame. (Time consuming)

2. Execute the constructor of HeartsGame3. Return the address of the newly created

object

0x40123456

0x40123456

0x40123456

Page 6: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Differentiate Stack and Heap Objects

Card a_card; // stack object Destructor will be implicitly called when a_card is

out of scope. Card* card_ptr = new Card; // heap object

Requires explicit delete Don’t do these

delete &a_card; a_card = *(new Card);

Page 7: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

When to use “new”? Objects have to live beyond current scope

Is object copying a viable solution? Good for copy : Card objects

Object size is small Non-polymorphic

Bad for copy : Player, Game objects. Involve heap memory allocation Polymorphic

Is object swapping a good alternative? Good for objects with embedded heap objects such as

Player, std::string All STL containers, like

std::vector, std::set, std::map, …, etc. Not useful for polymorphic classes

Game* makeGame(const char* name) { Game* result = 0; if (strcmp(name,”bridge”)==0) result= new BridgeGame; else if (strcmp(name,”hearts”)==0) result= new HeartsGame; return result;}

Page 8: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Problem with the interaction between “new” and exception

Player* Game::add_player(const char* name) {

Player* p = std::find_if(players,player+size, lessThan(name));

if (p != player+size && p->name() ==name)

return p;

Player* temp = new Player[size+1];

Player* dest = std::copy(players, p, temp);

*dest = Player(name);

std::copy(p, player+size, dest+1);

std::swap(temp, players);

delete[] temp;

return dest;

}players+size

name=“Ken”

pplayers TedSueJoeBob

p

tempTedSue

dest

Ken

JoeBob

Could throw exceptions

temp won’t be deleted if any exception is thrown

Page 9: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

How to fix it?

Use “try” and “catch”, delete temp in catch clause if any exception is thrown.

If non-thrown swap() is defined for Player class, you can use swap() instead of assignment. Only deal with std::bad_alloc exception Not a solution for other possible exceptions

Resource Acquisition is Initialization (RAII)

Page 10: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Resource Acquisition is Initialization (RAII) Referred as “Guard Idiom” by Dr. Gill.

However, the term RAII is more widely used in C++ community.

Relies on Stack object would be destructed upon out of

scope. Use stack object to hold the ownership of a heap

object, or any other resource that requires explicit clean up.

Heap object ( or resources) is release upon the destruction of the RAII stack object.

Page 11: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

RAII Utility Classes

template <class T> class scoped_ptr { T* ptr;public : scoped_ptr(T* p=0) :ptr(p) {} ~scoped_ptr() { delete ptr; } void reset(T* p) { delete ptr; ptr = p; } T* get() { return ptr;} void swap(scoped_ptr& );}

template <class T> class vector { T* ptr; size_t sz;public : vector() :ptr(0), sz(0) {} ~ vector () { delete ptr[]; } … void swap(vector& );}

Page 12: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

How to use RAII

Game* game;

if (strcmp (argv[1], “bridges”)==0) game = new BridgeGame ;else if (strcmp(argv[1],”hearts”)==0) game = new HeartsGame ;

game->add_player(“tom”);game->add_player(“ted”);

game->refresh();game->deal_hands();game->score_hands();game->print();

delete game;

boost::scoped_ptr<Game> game;

if (strcmp (argv[1], “bridges”)==0) game.reset(new BridgeGame);else if (strcmp(argv[1],”hearts”)==0) game.reset(new HeartsGame);

game->add_player(“tom”);game->add_player(“ted”);

game->refresh();game->deal_hands();game->score_hands();game->print();

Page 13: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Stock Smart Pointers

Boost (www.boost.org) scoped_ptr shared_ptr weak_ptr

std auto_ptr

Page 14: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

boost::scoped_ptr

Used for a single object, not for array Noncopyable

scoped_ptr<Game> game1(new BridgeGame); // OK

scope_ptr<Game> game2(game1); // Won’t compile

scoped_ptr<Game> game3; // OK

game3 = game1; // Won’t compile

game3.reset(new BridgeGame); // OK

game3.swap(game1); // OK

Useful to implement pointer to implementation (pimple) idiom.

Page 15: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

C++ Pimple idiom(Conceptual View)

+method1()+method2()

<<interface>>Foo

+method1()+method2()

-state

<<implementation class>>FooImpl

1

-pimpl

1

pimpl->method1();

Page 16: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

C++ Pimple idiom(implementation View)

/// Foo.h#include <boost/scoped_ptr.hpp>

struct FooImpl; // forward declarationclass Foo { using namespace boost; scoped_ptr<FooImpl> pimpl; public: Foo(); void method1();};

/// Foo.cc#include “Foo.h”

struct FooImpl { int state; …};

Foo::Foo() : pimpl(new FooImpl) {}

void Foo::method1() { ++ pimpl->state; …}

Page 17: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Why use pimple idiom

Separation between interface and implementation Allows library vendors keep implementation detail

secrete Avoid client recompilation for the change of

internal data structure. Exception safety

Page 18: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

std::auto_ptr

Similar to scoped_ptr, but copyable. Copy represents ownership transfer. Best used for function parameters and return

types in the case when ownership transfer is needed.

auto_ptr<Game> makeGame(const char* name) {if (strcmp(name, “bridge”) ==0)

return auto_ptr<Game>(new BridgeGame); else if (strcmp(name, “hearts”) == 0)

return auto_ptr<Game>(new HeartsGame); return auto_ptr<Game>();}

Page 19: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

boost:shared_ptr• Reference counting semantics.• Simplest form of garbage collection

a : share_ptr<Foo>

ptrpcount :Foo

1 : int

-ptr : T*-pcount : int*

shared_ptr

T

Class ViewObject View

b : share_ptr<Foo>

ptrpcount

shared_ptr<Foo> a(new Foo);shared_ptr<Foo> b = a;

2 : int0 : int

Page 20: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Reference Counting and Cyclic Dependency Problem

Deparment Course

1*

belongs to

offers

class Department { vector<share_ptr<Course> > courses; }

class Course { share_ptr<Course> department; }

Page 21: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Reference Counting and Cyclic Dependency Problem

void fun() { shared_ptr<Department> cse(new Department); shared_ptr<Course> cse332(new Course); cse332.department = cse; cse.courses.push_back(cse332);}

csecse332

: Department : Course

1 : int 1 : int

courses department

2 : int

courses[0]

2 : int

Page 22: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Boost::weak_ptr

Used together with shared_ptr to avoid cyclic reference counting problem.

Does not increase reference count during construction and copying.

Class Department { vector<shared_ptr<Course> > courses;};

Class Course { weak_ptr<Department> department;};

void fun() { shared_ptr<Department> cse(new Department); shared_ptr<Course> cse332(new Course); cse332.department = cse; cse.courses.push_back(cse332);}

The reference count is not incremented

Page 23: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Collection of Objects Player* players = new Player[size];

Need explicit delete statement for players array vector<Player> players;

Expensive Player object copying vector<Player*> players;

May require explicit delete statement for each individual Player object

vector<scoped_ptr<Player> > and vector<auto_ptr<Player> > won’t compile. because all containers in C++ standard requires that any

element a and b in the container, After a = b then a == b

Page 24: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Collection of Objects

vector<boost::shared_ptr<Player> > players Ownership to Player objects are not exclusive Comparing to vector<Player*>, it incurs small overhead for

reference counting. No need for explicit delete statement.

boost::ptr_vector<Player> players; Only heap objects can be added to the container. It has the exclusively ownership for the objects it

contains. No need for explicit delete statement.

Page 25: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Preferred Form for Object Collections

vector<int>, vector<Card> Contained objects are cheap to copy

ptr_vector<Player>, ptr_vector<Game> Contained objects are expensive to copy or

polymorphic. vector<shared_ptr<Player> > or

vector<share_ptr<Game> > The ownership of the contained object cannot be

exclusively held.

Page 26: Writing Correct C++ Programs without “delete” Huang-Ming Huang CSE332 Guest Lecture Washington University in St. Louis.

Summary

Dynamic memory allocation are expensive, avoid it if you can.

Be careful with implicit object copying. Don’t write code that requires explicit “delete”

statement. For single object : use smart pointers For collection of value types : use standard

container classes For collection of pointers : use boost ptr

containers or shared_ptr.