+ All Categories
Home > Documents > Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Date post: 18-Dec-2015
Category:
Upload: alisha-patrick
View: 224 times
Download: 2 times
Share this document with a friend
Popular Tags:
96
Effective C++ Effective C++ 55 Specific Ways to 55 Specific Ways to Improve Your Programs Improve Your Programs and Designs and Designs
Transcript
Page 1: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Effective C++Effective C++55 Specific Ways to 55 Specific Ways to

Improve Your Programs Improve Your Programs and Designsand Designs

Page 2: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Why???Why???

“Used without discipline, however, C++ can lead to code that is incomprehensible, unmaintainable, inextensible, inefficient and just plain wrong.”

Page 3: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

What’s it all about?What’s it all about?

Decisions, Decisions, Decisions

inheritance or templates?

public or private inheritance?

private inheritance or composition?

member or non-member functions?

DESIGN PATTERNS!

Page 4: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

What’s it all about? (2What’s it all about? (2ndnd category) category)

How do I do this correctly?

should destructor be virtual?

what return type to use?

what should operator do when it can’tget enough memory?

Page 5: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

TerminologyTerminology

• Declarations – name & typeextern int x; // object declaration

// function declaration, also signature

std::size_t numDigits(int num);

class Widget; // class declaration

// template declaration

template<typename T>

class GraphNode;

Page 6: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Terminology (continued)Terminology (continued)• Definition – details

int x; // object definition – memory

// function definition - bodystd::size_t numDigits(int num)

{ … }

// class and template definitions, include// methods and dataclass Widget{

public: // list of methods, data…

};template <typename T>class GraphNode {

public: // list of methods, data ..

};

Page 7: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Terminology (continued)Terminology (continued)

• Initialization – give first value• Default constructor – no arguments

(may be constructor with all default arguments)

• Recommendation: make constructor explicit, to avoid implicit type conversions (example next slide)

Page 8: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Explicit ExampleExplicit Exampleclass A{public: A();};class B{public: explicit B(int x=0,

bool b=true);};

class C{public: explicit C(int x); // not a default // constructor};

void doSomething(B bObj);

// in main…B bObj1; doSomething(bObj1); // fine

B bObj2(28); doSomething(bObj2); // fine

doSomething(28); // error!doSomething(B(28)); // fine, uses B constructor// explicitly

Page 9: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Copy Constructor/Copy AssignmentCopy Constructor/Copy Assignment

class Widget {public: Widget(); Widget(const Widget& rhs); Widget& operator =(const Widget& rhs); …};

Widget w1; // invoke default constructorWidget w2(w1); // invoke copy constructorw1 = w2; // invoke assignmentWidget w3 = w2; // invoke copy constructor!

Why?

Page 10: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #1Item #1

View C++ as a federation of languages:• C – blocks, statements, preprocessor, arrays,

pointers, etc.• C++ - OO, classes, encapsulation, inheritance,

polymorphism, virtual functions• Template C++ - generic programming, often with

special rules• STL – containers, iterators, algorithms, function

objects, using templates and specific conventions

Rules for effective C++ vary, depending on the part of C++ you are using.

Page 11: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

And now…And now…

On to some items!

Page 12: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #2Item #2

Prefer consts, enums and inlines to #defines (i.e., prefer compiler to preprocessor)

#define ASPECT_RATIO 1.653;

const double AspectRatio = 1.653;

• Symbolic names may be removed, don’t show up in error messages or debugging – confusing.

• Could have multiple copies of 1.653 in object code.

Page 13: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

More on constantsMore on constantsclass CostEstimate {private: static const double FudgeFactor; …};

const double CostEstimate::FudgeFactor = 1.35;

class GamePlayer {private: enum {NumTurns = 5; } int scores[NumTurns];};

enum “hack” – if need value for constantand compiler won’t allow

some compilers won’t allow values in“declaration” – must provide definition

remember static?

Page 14: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #3Item #3

Use const wherever possible

char greeting[] = “Hello”;

char *p = greeting; // non-const pointer, non-const data

const char *p2 = greeting;// non-const pointer, const char data

char * const p3 = greeting;// const pointer, non-const data

const char * const p4 = greeting;// const pointer, const data

Page 15: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Placement of constPlacement of const

• The following are equivalent:void f1 (const Widget *pw);void f2 (Widget const *pw);

• Iterators are similar to pointers:– a const_iterator is like const T*… iterator

can point to a different item, but item can’t be changed

– a const iterator is like T *const … iterator can’t point to a different item, but you can change the value of the item that it is pointing to

Page 16: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Consider making return value constConsider making return value const

const Rational { . . .

const Rational operator *(const Rational& lhs,

const Rational& rhs);

};

Rational a, b, c;

if (a * b = c) . . .

Meant to be ==

What will happen with const? without?

What happens with built-in types?

Page 17: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

May need two versionsMay need two versions• Overloaded [] operator, notice return by &, to allow modification• Can’t overload based on return type, but can overload based on

const vs. non-const member function

class TextBlock {public: . . .const char& operator[](std::size_t pos) const

{ return text[pos]; }char& operator[](std::size_t pos)

{ return text[pos]; }private: std::string text;};void print(const TextBlock& ctb){ std::cout << ctb[0]; // OK ctb[0] = ‘A’; // Not OK – compiler error}TextBlock tb(“hello”);tb[0] = ‘H’; // OK because return has &, not const

This is required to overload

Page 18: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Physical constness vs Logical constnessPhysical constness vs Logical constness

• Physical (bitwise) const: member function is const iff it doesn’t modify any of the bits inside the object

• Logical const: const member method might modify some bits in object, but only in ways clients cannot detect

• Compilers enforce bitwise constness, you should program using logical constness

Page 19: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Example: Problem with bitwise constExample: Problem with bitwise constclass CTextBlock {

public:

char& operator[](std::size_t pos) const

{return pText[pos]; }

private:

char *pText;

};

const CTextBlock cctb(“Hello”); // constant object

char *pc = &cctb[0]; // calls constant [] operator

*pc = ‘J’; // cctb is now “Jello”

violates logical constness, but compiler allows!

Page 20: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Modifying bits client doesn’t seeModifying bits client doesn’t seeclass CTextBlock {public: . . . std::size_t length() const;private: char *pText; std::size_t textLength; // last calculated length bool lengthIsValid; // whether length is valid};

std::size_t CTextBlock::length() const{ if (!lengthIsValid) { // error! changes bits textLength = std::strlen(pText); lengthIsValid = true; } return textLength;}

Page 21: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Mutable to the rescueMutable to the rescueclass CTextBlock {public: . . . std::size_t length() const;private: char *pText; mutable std::size_t textLength; mutable bool lengthIsValid;};

std::size_t CTextBlock::length() const{ if (!lengthIsValid) { textLength = std::strlen(pText); // OK now

lengthIsValid = true; } return textLength;}

Page 22: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExerciseExercise

• Begin C++ Exercise #1

Page 23: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Avoid duplication in const/non-constAvoid duplication in const/non-constclass TextBook {public: …const char& operator[](std::size_t pos) const {… // do bound checking… // log access data… // verify data integrityreturn text[pos];}char& operator[](std::size_t pos) {… // do bound checking… // log access data… // verify data integrityreturn text[pos];}private: std::string text;};

lots of duplicate code!

could put duplicated code in a function and call it – but thenhave duplicated calls to that function, and duplicated return

Page 24: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Cast-Away constCast-Away constclass TextBook {public: …const char& operator[](std::size_t pos) const {… // same as beforereturn text[pos];}

char& operator[](std::size_t pos) { return const_cast<char&>( static_cast<const TextBlock&>(*this)[position]);

}

private: std::string text;};

Now non-const [] just calls const

DO NOT go the other direction – not safe!

add const, to call const version of []safe conversion, so use static_castcasts covered in Item 27

const_cast needed to remove const before return

Page 25: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #4Item #4

Make sure that objects are initialized before they’re used.

The rules for when object initialization is guaranteed to take place are too complicated to be worth memorizing. Better practice to always initialize objects before use.

Page 26: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

InitializationInitialization• Make sure all constructors initialize everything in

the object. • Assignment is not the same as initialization.

ABEntry::ABEntry(const std::string&name, const std::list<PhoneNumber>& phones)

{ theName = name; thePhones = phones; numTimesConsulted = 0;}

default constructors were called for these prior to entering the body of the constructor – that’s when they were initialized. Not true for built-in types(e.g., numTimesCalled).

Page 27: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Initialization (continued)Initialization (continued)

• Prefer member initialization lists:ABEntry::ABEntry(const string& name, const list<PhoneNumber>& phones) : theName(name), thePhones(phones), numTimesConsulted (0) {}

• Single call to copy constructor is more efficient than call to default constructor followed by call to copy assignment.

• No difference in efficiency for numTimesConsulted, but put in list for consistency

Page 28: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Initialization (continued)Initialization (continued)

• Can do member initialization lists even for default construction:

ABEntry::ABEntry() : theName(), thePhones(), numTimesConsulted (0) {}

• Members are initialized in the order they are listed in class. Best to list them in that order in initialization list.

• Base classes are always initialized before subclasses.

Page 29: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Initialization of non-local static objectsInitialization of non-local static objects• Better to convert them to local static objects.

class FileSystem {public: std::size_t numDisks() const; . . .};extern FileSystem tfs; // declaration, must be defined

// in some .cpp in your library

class Directory{public Directory(params);};Directory::Directory(params) {…std::size_t disks = tfs.numDisks(); // use tfs object}

Has tfs been initialized?

Page 30: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Initialization of non-local (continued)Initialization of non-local (continued)class FileSystem { … } // as before

FileSystem& tfs() { static FileSystem fs; return fs;}class Directory{ … } // as before

Directory::Directory(params) {…std::size_t disks = tfs().numDisks(); // calls tfs function now} SINGLETON DESIGN PATTERN

non-local static objects replaced with local static object clients call functions instead of referring to object must ensure only one copy of FileSystem object (protected constructor)

Page 31: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

On to…On to…

Chapter Two

Constructors, Destructors and Assignment Operators

Page 32: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #5Item #5

Know what functions C++ silently writes and calls.class Empty{};

becomes:class Empty{

public:

Empty() { ... }

Empty(const Empty& rhs) { … }

~Empty() { … }

Empty& operator=(const Empty& rhs) {…}

};

Page 33: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

What do they do?What do they do?• Copy constructor and assignment generally do a

field-by-field copy.• These functions will not be written if your class

includes a const value or a reference value (compiler isn’t sure how to handle).

template <typename T>class NamedObject {public:NamedObject(std::string& name, const T& value);

private: std::string& nameValue; const T objectValue;};

Page 34: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #6Item #6

Explicitly disallow the use of compiler-generated functions you do not want

• By declaring member functions explicitly, you prevent compilers from generating their own version.

• By making a function private, you prevent other people from calling it. – don’t define them, so anyone who tries will get a linker error

• Even better, put functions in parent class, if child class attempts to call will generate a compiler error (earlier detection is better).

Page 35: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExampleExampleclass Uncopyable {protected: Uncopyable(); ~Uncopyable();private: Uncopyable(const Uncopyable&); Uncopyable& operator=(const Uncopyable&);};

class HomeForSale: private Uncopyable { … // class has no copy ctor or = operator};

Page 36: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #7Item #7

Declare destructors virtual in polymorphic base classes.

class TimeKeeper {public: TimeKeeper(); ~TimeKeeper(); …};class AtomicClock : public TimeKeeper { … };class WristWatch : public TimeKeeper { … };

TimeKeeper* getTimeKeeper(); // returns pointer to dynamically allocated objectTimeKeeper *ptk = getTimeKeeper(); // AtomicClock… // use it in some waydelete ptk; // release it

THE RESULTS OF THIS OPERATION ARE UNDEFINED!

Page 37: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

TimeKeeper continuedTimeKeeper continued

• Most likely AtomicClock part of object would not be destroyed – a “partially destroyed” object

• Solution:class TimeKeeper {

public:

TimeKeeper();

virtual ~TimeKeeper();

};

• Any class with virtual functions should almost certainly have a virtual destructor.

Page 38: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Don’t always make it virtual…Don’t always make it virtual…• If a class does not contain any virtual functions, often

indicates its not intended to be a base class. Making destructor virtual would be a bad idea.class Point {public: Point(int xCoord, int yCoord); ~Point();private: int x, y;};

• Point class can fit in 64-bit register, be passed as 64-bit value to other languages (C/Fortran)

• Virtual functions require objects to carry extra info for runtime binding. Typically a vptr (virtual table pointer) that points to an array of function pointers called a vtbl (virtual table).

• Point class will now be 96 bits (on 32-bit architecture)

Handy rule: declare a virtual destructor if and only if that class contains at least one other virtual function.

Page 39: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

When not to extend…When not to extend…• Be careful when you choose to extend a

class. std::string contains no virtual functions, so is not a good choice for a base class. STL container types also do not have

virtual destructors. class SpecialString : public std::string

{ … };

SpecialString *pss = new SpecialString(“Doomed”);

std::string *ps;

ps = pss;

….

delete ps; // UNDEFINED

Java can prevent programmers from extending a class… C++ can’t

Page 40: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

A destructor trickA destructor trick

• Maybe you have a class that you want to be abstract, but you don’t have any pure virtual functions.

• Make the destructor pure virtual• BUT – you still have to provide a definition,

because the compiler always calls the base class destructor. class AWOV {public:virtual ~AWOV()=0;};AWOV::~AWOV() {}

Page 41: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

But is it really polymorphic?But is it really polymorphic?

• The “handy rule” for base classes really applies only to polymorphic base classes – those designed to allow manipulation of derived class objects through base class interfaces.

• Not always the case – Uncopyable, for example, is designed to prevent copying. You wouldn’t do:Uncopyable *uc;uc = new HomeForSale();

Page 42: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #8Item #8

Prevent exceptions from leaving destructors

• Why? What if you had ten Widgets in an array and the ~Widget for the first Widget threw an exception. Then ~Widget is invoked for the next Widget in the array, and it throws an exception.

Page 43: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Example: DBConnectionExample: DBConnectionclass DBConnection{public: … // params omitted for simplicity static DBConnection create();

void close(); // may throw exception};

class DBConn { //manages DBConnectionpublic: …// destructor ensures db connection always closed ~DBConn() { db.close(); }private:};

Page 44: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

DBConnection (continued)DBConnection (continued)

• Allows clients to:{

DBConn.dbc(DBConnection::create());

… // use object

} // destructor called at end of block

• Problem: if db.close() fails, exception will be thrown in destructor

Page 45: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Options for destructorOptions for destructor• Terminate the program (OK if program cannot

continue to run after this type of error)

DBConn::~DBConn() {try { db.close() }catch(…) { make log entry that call failed std::abort();}

• Swallow the exception (usually a bad idea)

DBConn::~DBConn() {try { db.close() }catch(…) { make log entry that call failed}

Page 46: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

A better approach…A better approach…class DBConn { //manages DBConnectionpublic: void close() { // gives client option db.close(); closed = true; }

~DBConn() { if (!closed) { try {db.close();} // backup in case client didn’t catch ( . . ) {

make log entry that call failed … // terminate or swallow } } }private: DBConnection db; bool closed;};

Page 47: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #9Item #9

Never call virtual functions during construction or destruction

• The calls won’t do what you expect (differs from C# and Java!)

Page 48: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExampleExampleclass Transaction { // base classpublic: Transaction(); virtual void logTransaction() const =0; };Transaction::Transaction() { … logTransaction(); // final act is log transaction}class BuyTransaction : public Transaction {public: virtual void logTransaction() const;};class SellTransaction : public Transaction {public: virtual void logTransaction() const;};BuyTransaction b; calls Transaction constructor first, so when

logTransaction called – NOT one in BuyTransactionderived class members not initialized yet, so can’trun derived class functions.

at this point, every object is oftype Transaction!

Page 49: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

DiscussionDiscussion

• Would be easy to catch this one, because pure virtual so program wouldn’t link.

• If function were just virtual, “wrong” version would be called

Page 50: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #10Item #10Have assignment operators return a reference to

*this• Assignments can be chained:

int x, y, z;x = y = z = 15;

Right-associative, so this is like:x = (y = (z = 15));

Widget& operator =(const Widget& rhs){ … return *this;} Widget& operator +=(const Widget& rhs){ … return *this;}

Page 51: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #11Item #11

Handle assignment to self in operator=class Widget { . . . };

Widget w;

w = w;

ORa[i] = a[j]; //i=j, no so obvious

OR*px = *py;

Could even be pointer to parent class

Page 52: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Handling only self assignmentHandling only self assignment

Widget& Widget::operator =(const Widget& rhs)

{

if (this == &rhs) return *this;

delete pb;

pb = new Bitmap(*rhs.pb);

return *this;

}

• Handles self-assignment, but has the potential to generate an exception (for memory allocation).

Page 53: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

A better alternativeA better alternativeWidget& Widget::operator =(const Widget& rhs)

{

Bitmap *pOrig = pb;

pb = new Bitmap(*rhs.pb);

delete pOrig;

return *this;

}• If the call to new throws an exception, pb will be

unchanged. • For self-assignment, a copy is made of original before it is

deleted. Not the most efficient, but it works. Could put if statement back at top, if that’s a concern (but if it’s unlikely to be often, then better to avoid cost of test and branch, which are now required for every call).

Page 54: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #12Item #12

Copy all parts of an objectclass Customer {public: Customer(const Customer& rhs); Customer& operator=(const Customer& rhs);private: std::string name;};

Customer::Customer(const Customer& rhs) : name(rhs.name) { }

• But if a field is added to Customer class, and copy constructor and = operator not updated, copies will be partially constructed.

Page 55: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Can be worse with inheritance…Can be worse with inheritance…class PriorityCustomer : public Customer {

public:

PriorityCustomer(PriorityCustomer& rhs);

PriorityCustomer& operator = (PriorityCustomer& rhs);

private:

int priority;

};

PriorityCustomer::PriorityCustomer(const Customer& rhs) : priority(rhs.priority) { }

Not copying base class data members at all!

Page 56: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

What if base class members are private?What if base class members are private?

PriorityCustomer::PriorityCustomer(const Customer& rhs) : Customer(rhs), priority(rhs.priority) { }

PriorityCustomer& operator = (PriorityCustomer& rhs)

{

Customer::operator=(rhs);

priority=rhs.priority;

return *this;

}scope resolution to the rescue

handy use of initialization list

Page 57: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Chapter 3Chapter 3

Resource Management

Page 58: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #13Item #13

Use objects to manage resources.class Investment { … };

Investment *createInvestment();

// factory function, caller must delete pointer

// when finished using it

void f() {

Investment *pInv = createInvestment();

delete pInv;

}Looks OK, but what if premature return?OR, exception thrown?

Even if correct at first, what happens as code is maintained?

Page 59: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

A solution..A solution..

• Put the resource returned by createInvestment inside an object whose destructor will release the resource when control leaves f.

• std::auto_ptr is a smart pointer whose destructor automatically calls delete on what it points to

void f() {

std::auto_ptr<Investment> pInv(createInvestment());

… // use pInv as before

}

call to factory function

will delete pInv via auto_ptr’s destructor

Page 60: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

The idea…The idea…• Resources are acquired and immediately turned

over to resource-managing objects (e.g., the auto_ptr). RAII – Resource Acquisition Is Initialization.– Resource-managing objects use their destructors to

ensure that resources are released. Will happen regardless of how control leaves a block.

– There should never be more than one auto_ptr pointing to an object, or the object will be deleted more than once. Copying an auto_ptr sets it to NULL, so copying pointer has sole ownership.

std::auto_ptr<Investment> pInv1(createInvestment());std::auto_ptr<Investment> pInv2(pInv1); // pInv1 now NULLpInv1 = pInv2; // pInv2 now NULL

Page 61: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Another alternative…Another alternative…• Sometimes pointers must have “normal” copying

behavior (e.g., in container classes)• Use reference-counting smart pointer (RCSP).

Keeps track of how many objects point to resource, deletes it when nobody points to it. Similar to garbage collection, but can’t break cycles.

void f() { std::tr1::shared_ptr<Investment>

pInv1(createInvestment()); // count=1 std::tr1::shared_ptr<Investment> pInv2(pInv1); // both now point to same object, count = 2 pInv1 = pInv2; // still point to same object, count = 2}

pointers destroyed at end, object count will be 0 soit will be destroyed.

Page 62: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Don’t use these pointers with arraysDon’t use these pointers with arrays

• auto_ptr and tr1::shared_ptr use delete, not delete [] in their destructors

• Bad idea:

std::auto_ptr<std::string> aps(new std::string[10]);

std::tr1::shared_ptr<int> spi(new int[1024]);

• Boost library has classes for dealing with arrays.

• You can also create your own resource-managing class.

Page 63: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #14Item #14Think carefully about copying behavior in resource-

managing classes.• Example: Create a lock on a mutually exclusive itemclass Lock {

public:

explicit Lock(Mutex *pm) : MutexPtr(pm)

{ lock(mutexPtr); }

~Lock() {unlock(mutexPtr); }

private:

Mutex *mutexPtr;

};

Mutex m; // define mutex you need to use

{

Lock ml(&m); // lock mutex

… // critical section of code

} // mutex automatically unlocked

Notice the raw pointer, need to manage

Page 64: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Options if try to copyOptions if try to copy

• Prohibit copying. Might not make sense to have a copy – why would you have a copy of a Lock?

• Reference-count the underlying resource. Hold the resource until the last object using it has been destroyed. – tr1::shared_ptr does this. BUT, shared_ptr

deletes resource when reference count is 0, may not be what you want. Can specify “deleter” as second parameter to shared_ptr constructor.

Page 65: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Example with shared_ptrExample with shared_ptrclass Lock {public: explicit Lock(Mutex *pm) :

mutexPtr(pm, unlock) // unlock is deleter

{ lock(mutexPtr.get()); } // get described in Item #15

// No ~Lock needed, due to deleter with shared_ptr

private: std::tr1::shared_ptr<Mutex> mutexPtr; // no longer a raw pointer};

Page 66: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Two more optionsTwo more options

• Copy the underlying resource. Only purpose of resource-managing class is to ensure resource gets deleted. Makes a deep copy.

• Transfer ownership of the underlying resource. Used by auto_ptr in Item #13.

Page 67: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #15Item #15

Provide access to raw resources in resource-managing classes

tr1::shared_ptr and auto_ptr overload -> and * to allow implicit conversion to underlying raw pointer

Page 68: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExampleExampleclass Investment {public: bool isTaxFree() const; …};Investment* createInvestment(){ // factory function return std::auto_ptr<Investment>(new Investment());}

// store result in shared pointer:std::tr1::shared_ptr<Investment> pInv(createInvestment());

// pass raw pointer to daysHeld, get returns pointerint days = daysHeld(pInv.get());

// can also use ->bool taxable = !(pInv->isTaxFree());// Example with * and auto_ptrstd::auto_ptr<Investment> pi2(createInvestment());bool taxable2 = !((*pi2).isTaxFree());

Page 69: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #16Item #16

Use the same form in corresponding uses of new and delete

MyPoint *pointArray = new MyPoint[100];

delete pointArray;

std::string *stringPtr = new std::string;

delete [] stringPtr;

calls constructor 100 times

behavior undefinedprobably calls destructor once

behavior undefinedprobably calls destructor some numberof time (uses bit pattern where countshould be)

Page 70: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #17Item #17

Create newed objects in smart pointers in standalone statements

Example: consider two functions…int setPriority();void processWidget(std::tr1::shared_ptr<Widget> pw, int

priority);

Call to processWidget:processWidget(std::tr1::shared_ptr<Widget>(new Widget),

setPriority());Can leak resources. C++ compiler can determine order. If compiler

chooses the following order:1. Execute “new Widget”2. Call setPriority3. Call tr1::shared_ptr constructor. What if call to setPriority generates an exception? Memory leak!

Solution? Create Widget and store it, then pass it:std::tr1::shared_ptr<Widget> pw(new Widget);processWidget(pw, setPriority());

Page 71: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Chapter 4Chapter 4

Designs & Declarations

Page 72: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #18Item #18

Make interfaces easy to use correctly and hard to use incorrectly.

client

I want to use thisclass correctly

client

What mistakesmight client make?

Page 73: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExampleExampleclass Date {public: Date(int month, int day,int year);};

Date d(30, 3, 1995); // oops, should be 3, 30!Date d(3, 40, 1995); // just a typo

Page 74: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

A solutionA solution

struct Day{explicit Day(int d):val(d) {} int val; };struct Month{explicit Month(int m):val(m) {} int val; };struct Year{explicit Year(int y):val(y) {} int val; };

class Date {public: Date(const Month& m, const Day& d, const Year& y);

Date d(30, 3, 1995); // error! wrong types (int != Day)Date d(Day(30), Month(3), Year(1995)); // error! wrong

typesDate d(Month(3), Day(30), Year(1995)); // okDate d(Month(3), Day(40), Year(2001)); // oops…

could also use classes

struct review: struct Day { int val;};structs can have constructors, too!

Page 75: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

A way to restrict values…A way to restrict values…class Month {

public:

static Month Jan() {return Month(1); }

static Month Feb() {return Month(2); } …

static Month Dec() {return Month(12); }

private:

// prevent creation of new Month values

explicit Month(int m);

};

Date d(Month::Mar(), Day(30), Year(1995));

Page 76: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Be consistent with built-in typesBe consistent with built-in types

• Few characteristics lead to interfaces that are easy to use correctly as much as consistency

• Few characteristics lead to aggravating interfaces as much as inconsistency.

Why did it do that? **(&&*T

C++ STL:every container has size

Java array: length propertyJava List: size methodJava ArrayList: Count propertyAARGH!!!!

Page 77: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #19Item #19

Treat class design as type design• How should objects of your new type be created

and destroyed?• How should object initialization differ from object

assignment?• What does it mean for objects of your new type to

be passed by value?• What are the restrictions on legal values for your

new type? (invariants, exceptions)• Does your new type fit into an inheritance graph?

(virtual/non-virtual)• What kind of type conversions are allowed for

your new type? (implicit/explicit)

Page 78: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

More type considerationsMore type considerations• What operators and functions make sense for

your new type? (member/non-member)• What standard functions should be disallowed?• Who should have access to members of your new

type? (friends/nested)• What is the “undeclared interface” of your new

type? (performance, safety, resource usage)• How general is your new type? (templates)• Is a new type really what you need? (maybe a few

non-member functions would do)

Page 79: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #20Item #20

Prefer pass-by-reference-to-const to pass-by-value

class Person {public: Person();virtual ~Person();private: std::string name; std::string address;};class Student: public Person {public: Student(); virtual ~Student();private: std::string school; std::string schoolAddr;};

Student plato;

pass by value

bool validateStudent(Student);

construct Personconstruct nameconstruct addressconstruct Studentconstruct schoolconstruct schoolAddr

destruct Persondestruct namedestruct addressdestruct Studentdestruct schooldestruct schoolAddr

Page 80: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Avoid the Avoid the slicingslicing problem… problem…

• When derived class object is passed by value as base class object, special features of child class are “sliced” off

class Window {public: std::string name() const; virtual void display() const;};

class ScrollWindow:public Window {public: virtual void display() const;};

void printNameDisplay(const Window& w){ std::cout << w.name(); w.display();}

ScrollWindow sw;

void printNameDisplay(Window w){ std::cout << w.name(); w.display();}

calls Windowdisplay

calls ScrollWindowdisplay

Page 81: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Does size matter?Does size matter?

Even small objects should be passed by const reference

• Some compilers won’t put objects in registers, even if only data is one double

• Size of object may change• May be only a pointer, but then will need

to copy everything pointed to• Different implementation of C++ may be

larger (some implementations of standard library string type are seven times as big as others)

Page 82: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #21Item #21

Don’t try to return a reference when you must return an object

Here’s anobject

There’s nothingthere!

Page 83: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExampleExampleclass Rational {

public:

Rational(int num=0, int denom=0);

private:

int n, d;

friend const Rational operator*(const Rational& lhs, const Rational& rhs);

};

Rational a(1, 2);

Rational b(3, 5);

Rational c = a * b; // 3/10

Is return-by-value necessary?

Must create c

//Bad option:const Rational& operator*…{ Rational result(lhs.n*rhs.n,

lhs.d * rhs.d); return result;}

Returns pointer to localvariable, which won’t exist!

Page 84: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Example, continuedExample, continued// Another bad optionconst Rational& operator*…{ Rational *result = new Rational(lhs.n*rhs.n, lhs.d * rhs.d); return *result;}

Still have constructor call, which you wanted to avoid, ANDwho will call delete??

Rational w, x, y, z;

w = x * y * z;

Two calls to *, no way to get at pointers, even if want to be conscientious.

// Yet another bad optionconst Rational& operator*…{ static Rational result; result = … return result;}

bool operator==(const Rational& lhs, const Rational& rhs);

Rational a, b, c, d; …

if ((a * b) == (c * d)) …

Always evaluates to true. if (operator==(operator*(a, b), operator*(b, c))

static == static

Page 85: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Example, concludedExample, concluded

• The right way to write a function that must return a new object is to have that function return a new object.

inline const Rational operator*(const Rational& lhs, const Rational& rhs)

{

return Rational(lhs.n*rhs.n, lhs.d*rhs.d);

}

compiler may optimize, to avoid cost of constructor/destructor

Page 86: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #22Item #22

Declare data members private• Why not public members?

– consistency. If everything is a function, won’t have to remember when to use ()

– control. Can provide no access, read-only access, or read-write access.

– encapsulation. implementation can change, but interface remains the same. no changes needed to client code.

Page 87: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

ExampleExampleclass SpeedDataCollection {

public:

void addValue(int speed);

double averageSoFar() const;

};

Should averageSoFar return precomputed value, or compute its value each time it’s called?

If needed infrequently computing each time may be OK. If needed frequently and memory is not an issue, should

precompute and store, so just return a value. Either way, decision should be hidden from client.

PUBLIC == UNENCAPSULATED == UNCHANGEABLE

Page 88: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

What about protected?What about protected?

Are protected data members more encapsulated than public ones?

• Practically speaking, no. Encapsulation is inversely proportional to the amount of code that might be broken if that data member changes (e.g., is removed from class).

• For public member, that’s an unknowably large amount.

• For protected members which are accessed in derived classes, still an unknowably large amount. In general, too much code will need to be rewritten, recompiled etc. if you change that data member.

Page 89: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Other views on the topic…Other views on the topic…

• http://www.velocityreviews.com/forums/t286009-data-members-as-quotprotectedquot.html

Page 90: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #23Item #23

Prefer non-member non-friend functions to member functionsclass WebBrowser {public: void clearCache(); void clearHistory(); void removeCookies();

void clearBrowser();};

We want to clear cache, history and cookies in the browser. Would this be better as a member function? Or a non-member function?

void clearBrowser(WebBrowser& wb) { wb.clearCache(); wb.clearHistory(); wb.removeCookies();}

Non-member offers more encapsulation and more flexibility.

How??

Page 91: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Example continuedExample continued• Encapsulation

– Something that’s encapsulated is hidden from view– More something is encapsulated, fewer things can see it– Fewer things can see it, greater flexibility we have to

change it– Less code that can see (i.e., access) data the more data is

encapsulated, so the more freely we can change data characteristics

– Coarse-grained measure: count the number of functions that access it.

– Number of functions that can access private data is number of member functions + number of friend functions.

– Adding a non-member function doesn’t add to number of functions that can potentially “see” private data.

Page 92: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Convenience functionsConvenience functions

• Convenience functions may be grouped into different header files

• May all be in the same namespace

namespace WebBrowserStuff { class WebBrowser {…}; .. // core functionality}

//header webbrowserbookmarks.hnamespace WebBrowserStuff { ..//bookmark-related convenience fns}

//header webbrowsercookies.hnamespace WebBrowserStuff { ..//cookie-related convenience fns}

Partitioning functionality in this way is not possible when it comes from a class’s member functions.

Page 93: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #24Item #24

Declare non-member functions when type conversions should apply to all parameters

• For some classes allowing implicit type conversions makes sense – e.g., from integers to rationals

class Rational {public: Rational(int numerator=0, denominator=1); int numerator() const { return n; } int denominator() const { return d; }private: int n,d;};

ctor is not explicit

Page 94: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Making the case for non-member *Making the case for non-member *class Rational {public: const Rational operator *(const Rational& rhs) const;};

Remember const return – won’t allow (a*b=c) when want (a*b==c)

Rational oneEighth(1,8);Rational oneHalf(1,2);Rational result = oneHalf * oneEighth;

result = oneHalf * 2; // OKresult = 2 * oneHalf; // error!

result = oneHalf.operator *(2); // OKresult = 2.operator *(oneHalf); // error!

Page 95: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Should it be a friend?Should it be a friend?class Rational {

public:

};

const Rational operator *(const Rational& lhs, const Rational& rhs);

• Whenever you can avoid friend functions, you should. Why? Same reasons that you make variables private and as many functions as possible non-members

Page 96: Effective C++ 55 Specific Ways to Improve Your Programs and Designs.

Item #25Item #25

Consider support for a non-throwing swap.Typical swap algorithm (std::swap):template <typename T>

void swap(T& a, T& b)

{

T temp(a);

a = b;

b = temp;

}• Requires 3 copies: a to temp, b to a, temp to b• Copying not required for types consisting of pointer to

another type containing the real data• Read the book if you need this….


Recommended