Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Idiomatic C++

Idiomatic C++

A collection of most wanted and widely accepted idioms and coding conventions for C++ development presented along with examples and comments. The lecture targets performance oriented codes so emphasis is on performance-friendly techniques.

Federico Ficarelli

December 01, 2014
Tweet

Other Decks in Programming

Transcript

  1. Idiomatic C++
    Federico Ficarelli
    http://nazavode.github.io

    View full-size slide

  2. Overview
    ◼ Chapter 1: Design Issues
    ◼ Chapter 2: Construction/Destruction/Copying
    ◼ Chapter 3: Namespaces
    ◼ Appendix: Tools
    Federico Ficarelli, Idiomatic C++ 2

    View full-size slide

  3. Bibliography
    ◼ Herb Sutter and Andrei Alexandrescu, C++ Coding
    Standards
    ◼ Bjarne Stroustrup’s C++ Style and Technique FAQ
    [www.stroustrup.com/bs_faq2.html]
    ◼ stackoverflow.com, C++faq tag
    ◼ Microsoft Channel 9 [http://channel9.msdn.com/]
    ◼ Scott Meyers, Effective C++
    ◼ cppreference.com
    ◼ Bjarne Stroustrup, The C++ Programming
    Language
    Federico Ficarelli, Idiomatic C++ 3

    View full-size slide

  4. Chapter 1
    Design Issues:
    Idioms and Best Practices

    View full-size slide

  5. Resource management: the naive
    approach
    In order to guarantee resources
    release, we need to keep track of:
    ◼ natural return;
    ◼ return statements;
    ◼ exceptions thrown.
    Issues:
    ◼ code complexity;
    ◼ duplicated code (copy and paste);
    ◼ forces to catch and re-throw
    (even if we aren’t able to handle it);
    ◼ error prone.
    void foo ()
    {
    char* ch = new char[100];
    if (...)
    if (...)
    return;
    else if (...)
    if (...)
    else
    throw "ERROR";
    // This may not be invoked...
    delete [] ch;
    }
    void bar ()
    {
    lock.acquire();
    if (...)
    if (...)
    return;
    else
    throw "ERROR";
    // This may not be invoked...
    lock.release();
    }
    Federico Ficarelli, Idiomatic C++ 5

    View full-size slide

  6. Resource Handler Idiom: let the stack
    do the work
    template
    class AutoDelete
    {
    public:
    AutoDelete (T * p = 0) :
    ptr_(p) {}
    ~AutoDelete () throw() {
    delete ptr_;
    }
    private:
    T *ptr_;
    DISALLOW_COPY_AND_ASSIGN(AutoDelete);
    };
    class ScopedLock
    {
    public:
    ScopedLock (Lock & l) : lock_(l) {
    lock_.acquire();
    }
    ~ScopedLock () throw () {
    lock_.release();
    }
    private:
    Lock& lock_;
    DISALLOW_COPY_AND_ASSIGN(ScopedLock);
    };
    Exception Safety Rule:
    the only code that is guaranteed to be executed after an
    exception is thrown are the destructors of objects residing on
    the stack.
    Federico Ficarelli, Idiomatic C++ 6

    View full-size slide

  7. Resource Aquisition Is Initialization
    (RAII)
    void foo() {
    AutoDelete safe_del(new X());
    if (...)
    if (...)
    return;
    // No need to call delete here.
    // Destructor will delete memory
    }
    void bar() {
    ScopedLock safe_lock(l);
    if (...)
    if (...)
    throw "ERROR";
    // No need to call release here.
    // Destructor will release the lock
    }
    RAII:
    every time you need to wrap and manage a resource (memory, file, lock,
    etc...) in a class, let the constructor acquire and the destructor release it:
    the stack semantics will release the resource when it leaves the scope.
    Federico Ficarelli, Idiomatic C++ 7

    View full-size slide

  8. Resource Handler Idiom: file
    descriptor
    // Da Stroustrup’s C++ FAQ:
    class File_handle {
    FILE* p;
    public:
    File_handle(const char* n, const char* a) {
    p = fopen(n,a);
    if (p==0) throw errno;
    }
    File_handle(FILE* pp) {
    p = pp;
    if (p==0) throw errno;
    }
    ~File_handle() { fclose(p); }
    operator FILE*() { return p; }
    // ...
    };
    void f(const char* fn) {
    File_handle f(fn,"rw");
    // use file through f,
    // don't care about release
    // ...
    }
    Federico Ficarelli, Idiomatic C++ 8

    View full-size slide

  9. RAII: essential assumptions
    Single Responsibility Principle:
    every class should have a single, clear responsibility and that
    responsibility should be entirely encapsulated by the class.
    Constructor:
    it must acquire the managed resource and, in case of failure,
    raise a proper exception. The acquisition process must be RAII
    itself (without relying on the destructor).
    Destructor:
    it starts with a valid and constructed object (guaranteed by
    constructor) and must release the resource. It cannot fail.
    Federico Ficarelli, Idiomatic C++ 9

    View full-size slide

  10. RAII: downsides
    ◼ Automatic lifetime, tightly bound to stack
    semantics;
    ◼ each resource type needs a proper resource
    holder;
    ◼ strict ownership, resources cannot be (easily
    and cleanly) passed around.
    Federico Ficarelli, Idiomatic C++ 10

    View full-size slide

  11. Ownership: who owns you, baby?
    How can we explicitly express ownership in C++?
    Ownership Semantics:
    an object owns a resource when it has the responsibility to release
    that resource.
    struct Resource {
    void foo() { /* ... */}
    };
    Resource* get_resource() {
    return new Resource;
    // Ownership implicitly transferred to the caller
    }
    int main (void) {
    get_resource()->foo(); // ?
    }
    // Header file
    // ...
    // AMAZING documentation about
    // the following function.
    Resource* get_resource();
    // ...
    Federico Ficarelli, Idiomatic C++ 11

    View full-size slide

  12. Ownership semantics and smart
    pointers
    std::auto_ptr (C++03)
    std::unique_ptr (C++11)
    boost::scoped_ptr (no std)
    const std::auto_ptr (C++03)
    std::tr1::scoped_ptr (C++03)
    boost::shared_ptr (no std)
    std::tr1::shared_ptr (TR1)
    std::shared_ptr (C++11)
    boost::weak_ptr (no std)
    std::tr1::weak_ptr (TR1)
    std::weak_ptr (C++11)
    Strict ownership, transfer allowed
    Strict ownership, transfer not allowed
    Shared (multiple) ownership
    Federico Ficarelli, Idiomatic C++ 12

    View full-size slide

  13. Strict ownership: auto_ptr
    ◼ Same semantics as raw ptr;
    ◼ when the owner goes out of
    scope, raw ptr is destroyed
    (operator delete);
    ◼ we can release ownership
    and take back the raw ptr.
    Strict Ownership, Transfer Allowed:
    the auto_ptr has semantics of strict ownership, meaning that there is only one
    auto_ptr instance responsible for the object's lifetime. If an auto_ptr is copied, the
    source loses the reference.
    #include
    int main() {
    std::auto_ptr pt( new T );
    } // <-- ~T()
    #include
    int main() {
    T* pt1 = new T;
    // pass ownership to an auto_ptr
    std::auto_ptr pt2( pt1 );
    *pt2 = 12; // same as "*pt1 = 12;
    pt2->SomeFunc(); // same as "pt1->SomeFunc();
    // use get() to see the pointer value
    assert( pt1 == pt2.get() );
    // use release() to take back ownership
    T* pt3 = pt2.release();
    // no owner, no auto-delete!
    delete pt3;
    }
    Federico Ficarelli, Idiomatic C++ 13

    View full-size slide

  14. Strict ownership: auto_ptr
    int main() {
    auto_ptr pt( new T(1) );
    pt.reset( new T(2) );// ~T(1), owns T(2)
    } // ~T(2)
    int main() {
    auto_ptr pt1( new T ); // pt1 owns
    auto_ptr pt2; // pt2 non-owning
    pt1->DoSomething(); // ok
    pt2 = pt1; // pt1 -> pt2
    pt2->DoSomething(); // ok
    pt1->DoSomething(); // !!!
    } // ~T()
    ◼ Ownership can be explicitly dropped and set
    on the fly (reset);
    ◼ a non-owning auto_ptr has the same
    semantics as NULL pointer: never dereference
    it (check with operator bool).
    Federico Ficarelli, Idiomatic C++ 14

    View full-size slide

  15. Strict ownership: auto_ptr
    ◼ No custom deleter, can manage objects
    allocated with operator new only;
    ◼ copying and assigning changes the owner of a
    resource, modifying not only the lhs but also
    the rhs, which breaks assignment semantics;
    ◼ cannot be used in stl
    containers.
    std::vector< std::auto_ptr > v;
    /* ... */
    std::sort( v.begin(), v.end() ); // ?
    Federico Ficarelli, Idiomatic C++ 15

    View full-size slide

  16. Strict ownership: scoped_ptr
    ◼ Used to show explicit ownership;
    ◼ supports custom deleter;
    ◼ useful for automatic deletion of local objects
    or class members (PIMPL, RAII, etc...)
    ◼ can be “simulated” using the
    const auto_ptr Idiom.
    Strict Ownership, Transfer Not Allowed:
    the scoped_ptr has semantics of strict ownership, meaning that there is only one
    scoped_ptr instance responsible for the object's lifetime. The owning scoped_ptr
    cannot be copied, ownership cannot be transferred.
    const auto_ptr pt1( new T );
    auto_ptr pt2( pt1 ); // illegal
    auto_ptr pt3;
    pt3 = pt1; // illegal
    pt1.release(); // illegal
    pt1.reset( new T ); // illegal
    Federico Ficarelli, Idiomatic C++ 16

    View full-size slide

  17. Shared ownership: shared_ptr
    Shared Ownership:
    the shared_ptr has semantics of multiple ownership, meaning that multiple owning
    instances are allowed at a time. The instance is reference counted*: it will be destroyed
    only when the last owner is released.
    ◼ Useful when object’s lifetime is complex and
    not tied to a particular scope/object;
    ◼ supports custom deleter;
    ◼ can be safely used inside stl containers.
    Federico Ficarelli, Idiomatic C++ 17

    View full-size slide

  18. Shared ownership: shared_ptr
    int main() {
    typedef std::tr1::shared_ptr sh_ptr;
    sh_ptr p1;
    {
    sh_ptr p2(new T()); // ref = 1
    p1=p2; // ref = 2
    } // ref = 1
    } // ref = 0 -> ~T()
    Reference count semantics
    template
    class ArrayDeleter {
    public:
    void operator() (T* d) const {
    delete [] d;
    }
    };
    std::tr1::shared_ptr
    array (new double[256], ArrayDeleter());
    Custom deleter
    Federico Ficarelli, Idiomatic C++ 18

    View full-size slide

  19. shared_ptr: Decorator Idiom
    template class decorator {
    private:
    T * p_;
    public:
    explicit pointer(T * p): p_(p) {}
    shared_ptr operator->() const {
    p_->prefix();
    return std::tr1::shared_ptr(p_, std::mem_fn(&T::suffix));
    }
    };
    class X {
    private:
    void prefix();
    void suffix();
    friend class decorator;
    public:
    void f();
    void g();
    };
    int main() {
    X x;
    decorator px(&x);
    px->f();
    px->g();
    }
    Federico Ficarelli, Idiomatic C++ 19

    View full-size slide

  20. Latent ownership: weak_ptr
    ◼ Expresses «weak» (latent) ownership;
    ◼ must be casted to shared_ptr before actual
    use (no operators);
    ◼ essential to break reference cycles.
    typedef std::tr1::shared_ptr sh_ptr;
    struct T {
    sh_ptr other;
    };
    void test() {
    sh_ptr p1 (new T());
    sh_ptr p2 (new T());
    p1->other = p2; // p1 -> p2
    p2->other = p1; // p2 -> p1
    } // ?
    typedef std::tr1::shared_ptr sh_ptr;
    typedef std::tr1::weak_ptr wk_ptr;
    struct T {
    wk_ptr other;
    };
    void test() {
    sh_ptr p1 (new T());
    sh_ptr p2 (new T());
    if( sh_ptr p = p1->other.lock() ) {
    p(p2); // p1 -> p2
    }
    if( sh_ptr p = p2->other.lock() ) {
    p(p1); // p2 -> p1
    }
    } // ?
    Federico Ficarelli, Idiomatic C++ 20

    View full-size slide

  21. Removing ambiguity, improving
    robustness
    ◼ Makes factory
    functions ownership
    explicit;
    ◼ use any type of smart
    ptr (depending on your
    needs);
    ◼ improves robustness
    dramatically.
    // Header file
    // ...
    // AMAZING documentation about
    // the following function.
    std::auto_ptr get_resource();
    // ...
    struct Resource {
    void foo() { /* ... */}
    /* ... */
    };
    std::auto_ptr get_resource() {
    return std::auto_ptr( new Resource );
    // Ownership EXPLICITLY transferred
    // to the caller
    }
    int main (void) {
    get_resource()->foo(); // ~Resource()
    }
    Resource Return Idiom:
    never return raw pointers from within functions; prefer conveying resource ownership
    explicitly in the return type.
    Federico Ficarelli, Idiomatic C++ 21

    View full-size slide

  22. Header files: how make them safe
    Header files should be written to:
    ◼ be self-sufficient;
    ◼ be portable;
    ◼ minimize dependencies;
    ◼ avoid pollution of client’s names search
    space.
    Federico Ficarelli, Idiomatic C++ 22

    View full-size slide

  23. Header files: (usually) a mess
    #ifndef _DATE_H_
    #define _DATE_H_
    #include
    #include
    #include
    using namespace std;
    namespace calendar {
    class Date
    {
    private:
    int month_, day_, year_;
    friend ostream & operator<<
    (ostream &os, const Date& d);
    public:
    Date() {
    set_date( 1, 1, 1970 );
    }
    Date(int month, int day, int year) {
    set_date(month, day, year);
    }
    // ...
    void set_date(int month, int day, int year) {
    month_ = month;
    day_ = day;
    year_ = year;
    }
    inline void Convert(MuslimDate* other) {
    // Some heavy work...
    // from math.h:
    double res = exp(somevalue);
    // ...
    *other = MuslimDate(/*...*/)
    }
    inline int get_month() { return month_; }
    inline get_day() { return day_; }
    inline get_year() { return year_; }
    };
    ostream & operator<<(ostream &os, const Date& date)
    {
    return os << day_ << "/"
    << month_ << "/" << year_;
    }
    } // namespace calendar
    #endif // _DATE_H_
    Federico Ficarelli, Idiomatic C++ 23

    View full-size slide

  24. Header files: pure header and the
    Inline Guard Idiom
    // date.h
    #ifndef IDIOMATICPP_DATE_H_16032013 // <--
    #define IDIOMATICPP_DATE_H_16032013
    #include // <--
    namespace calendar {
    class MuslimDate; // <-- Forward
    class Date
    {
    private:
    int month_, day_, year_;
    friend std::ostream & operator<<
    (std::ostream &os, const Date& d);
    public:
    Date();
    Date(int month, int day, int year);
    void set_date(int month, int day, int year);
    void Convert(MuslimDate* other);
    int get_month();
    int get_day();
    int get_year();
    };
    // ...
    // Inline Guard Idiom
    #if defined(IDIOMATICCPP_USE_INLINE)
    #define INLINE inline
    #include
    #endif
    } // namespace calendar
    #endif // IDIOMATICPP_DATE_H_16032013
    ◼ Guards: avoid clashes and
    reserved identifiers;
    ◼ prefer forward declarations;
    ◼ remove unused inclusions;
    ◼ remove using statements;
    ◼ be sure that the pure header
    is made of declarations only.
    Federico Ficarelli, Idiomatic C++ 24

    View full-size slide

  25. Header files: inline-only
    // date-inl.h
    #ifndef IDIOMATICPP_DATE_INL_H_16032013 // <--
    #define IDIOMATICPP_DATE_INL_H_16032013
    #include
    #include // <--
    #include
    INLINE void Date::Convert(MuslimDate* other) {
    // Some heavy work...
    double res = std::exp(somevalue); // <--
    // ...
    *other = MuslimDate(/*...*/)
    }
    INLINE int Date::get_month() { return month_; }
    INLINE int Date::get_day() { return day_; }
    INLINE int Date::get_year() { return year_; }
    #endif // IDIOMATICPP_DATE_INL_H_16032013
    ◼ Collect all inline
    definitions in a separate
    header;
    ◼ beware of C std
    headers;
    ◼ no using statements
    (qualify all identifiers);
    ◼ be sure that the inline
    header is made of inline
    definitions only.
    Federico Ficarelli, Idiomatic C++ 25

    View full-size slide

  26. Header files: complete Inline Guard
    #include
    #if !defined(IDIOMATICCPP_USE_INLINE) // <--
    #define INLINE
    #include
    #endif
    #include
    using namespace std; // <--
    using namespace calendar;
    Date::Date() { set_date( 1, 1, 1970 ); }
    Date::Date(int month, int day, int year) {
    set_date(month, day, year);
    }
    void Date::set_date(int month, int day, int year) {
    month_ = month;
    day_ = day;
    year_ = year;
    }
    ostream & operator<<(ostream &os, const Date& date)
    {
    return os << day_ << "/"
    << month_ << "/" << year_;
    }
    // ...from date.h:
    // ...
    // Inline Guard Idiom
    #if defined(IDIOMATICCPP_USE_INLINE)
    #define INLINE inline
    #include
    #endif
    // ...
    ◼ Put the Idiom’s flip
    side in a single
    implementation file;
    ◼ inlining can be
    controlled by a proper
    macro definition.
    Federico Ficarelli, Idiomatic C++ 26

    View full-size slide

  27. Decoupling
    ◼ The vast majority of header-related idioms
    care about decoupling;
    ◼ the main goal is to reduce dependencies and
    build time;
    ◼ accepted downsides could impact
    performance.
    Federico Ficarelli, Idiomatic C++ 27

    View full-size slide

  28. Decoupling: Interface Class Idiom
    ◼ Classical OO approach to decoupling;
    ◼ invokes implementation of an abstraction/class
    using runtime polymorphism;
    Dependency Inversion Principle:
    implementation classes should not depend on each other. Instead, they should
    depend on common abstraction represented using an interface class.
    Federico Ficarelli, Idiomatic C++ 28

    View full-size slide

  29. Interface Class Idiom
    // Abstract base class -> Interface
    class Exporter {
    public:
    virtual std::string
    toString(Document* doc) = 0;
    };
    // Concrete interface implementors (Liskov)
    class CSVExporter : public Exporter {
    public:
    std::string toString(Document* doc)
    { /* ... */ }
    };
    class XMLExporter : public Exporter {
    public:
    std::string toString(Document* doc)
    { /* ... */ }
    };
    // Client (Open Close Principle)
    class ExportController {
    private:
    Exporter* m_exporter; // <--
    public:
    void setExporter(Exporter* exporter);
    void runExport();
    };
    void ExportController::runExport() {
    Document* currentDocument =
    GetCurrentDocument(); // <-- Factory
    // (no ctors)
    String exportedString =
    m_exporter->toString(currentDocument);
    String exportFilePath = GetSaveFilePath();
    WriteStringToFile(exporterString, exportFilePath);
    }
    ◼ DIP: ExportController (higher level interface) has no
    knowledge of any Exporter subclass (lower level interface);
    ◼ both depend on abstract Exporter interface (the common
    abstraction).
    Federico Ficarelli, Idiomatic C++ 29

    View full-size slide

  30. Private Implementation (PIMPL)
    ◼ The language makes private members
    inaccessible but not invisible;
    ◼ idiom meant to completely decouple
    interface (and clients) from implementation;
    ◼ implements a true compilation firewall;
    ◼ consider carefully the advantages (build time,
    insulation) and downsides (extra indirection
    level).
    Federico Ficarelli, Idiomatic C++ 30

    View full-size slide

  31. Private Implementation (PIMPL)
    // myclass.h:
    class MyClass {
    private:
    struct MyClassPIMPL; // Forward
    MyClassPIMPL* pimpl_; // Handle
    public:
    Handle();
    Handle(const Handle&);
    Handle& operator=(const Handle&);
    ~Handle();
    // Other operations...
    };
    // myclass.cpp:
    #include "myclass.h"
    struct MyClass::MyClassPIMPL {
    int a, b;
    };
    MyClass::MyClass()
    : pimpl_(new MyClassPIMPL()) {
    // do nothing
    }
    MyClass::MyClass(const MyClass& other)
    : pimpl_(new MyClassPIMPL(*(other.pimpl_))) {
    // do nothing
    }
    MyClass& MyClass::operator=(const MyClass &other) {
    delete pimpl_;
    *pimpl_ = *(other.pimpl_);
    return *this;
    }
    MyClass::~MyClass() {
    delete pimpl_;
    }
    // myclass.h:
    class MyClass {
    private:
    struct MyClassPIMPL;
    shared_ptr pimpl_;
    public:
    // ...
    };
    Federico Ficarelli, Idiomatic C++ 31

    View full-size slide

  32. Functions: avoid making friends
    ◼ Improves encapsulation by minimizing
    dependencies: the function cannot depend non-
    public members (“Don’t give away your
    internals”);
    ◼ breaks apart monolithic classes to liberate
    separable functionalities, reducing coupling.
    Function Placement:
    when implementing new functionalities, prefer non-member non-friend
    functions.
    When implementing functionalities, the common belief is
    that OO prefers members.
    Federico Ficarelli, Idiomatic C++ 32

    View full-size slide

  33. Functions: avoid making friends
    class NetworkBuffer {
    public:
    bool empty() {
    return
    this->device->get_packets_count() == 0;
    }
    /* ... */
    };
    class List {
    public:
    bool empty() {
    return
    this->length == 0;
    }
    /* ... */
    };
    class NetworkBuffer {
    public:
    size_t size() {
    return
    this->device->get_packets_count();
    }
    /* ... */
    };
    class List {
    public:
    size_t size() {
    return this->length;
    }
    /* ... */
    };
    template
    bool empty( const T& s )
    {
    return s.size() == 0;
    }
    Federico Ficarelli, Idiomatic C++ 33

    View full-size slide

  34. Functions: avoid making friends
    if (f needs to be virtual)
    make f a member function of C;
    else if (f is operator>> or operator<<)
    {
    make f a non-member function;
    if (f needs access to non-public members of C)
    make f a friend of C;
    }
    else if (f needs type conversions on its left-most
    argument)
    {
    make f a non-member function;
    if (f needs access to non-public members of C)
    make f a friend of C;
    }
    else if (f can be implemented via C's public
    interface)
    make f a non-member function;
    else
    make f a member function of C;
    Functions: the Meyer’s Algorithm [Effective C++]
    Federico Ficarelli, Idiomatic C++ 34

    View full-size slide

  35. Design guidelines: most wanted
    1. Ensure resources are owned by objects. Use
    explicit RAII and smart pointers to expose
    ownership and enforce exception safety.
    2. Give one entity one cohesive responsibility.
    3. Keep your header files clean, don’t harm clients
    (nor yourself).
    4. PIMPL and DIP judiciously.
    5. Don’t optimize prematurely: correctness,
    simplicity and clarity come first.
    6. Don’t pessimize prematurely.
    Federico Ficarelli, Idiomatic C++ 35

    View full-size slide

  36. Chapter 2
    Construction/Destruction/Copying

    View full-size slide

  37. Construction/Destruction/Copying
    Design and implementation of the Big Four:
    ◼ default construction,
    ◼ copy construction;
    ◼ copy assignment,
    ◼ destruction.
    Pay attention:
    ◼ compiler can generate them for you;
    ◼ the language treats classes with value
    semantics by deafult.
    Federico Ficarelli, Idiomatic C++ 37

    View full-size slide

  38. Constructors: let them fail
    ◼ Throwing from within a constructor is the
    most safe and widespread technique;
    ◼ the «init method» technique is unsafe and
    breaks RAII and all the idioms discussed in
    this chapter.
    Failing Constructors:
    “You should throw an exception from a constructor whenever you cannot
    properly initialize (construct) an object. There is no really satisfactory alternative
    to exiting a constructor by a throw”. [Stroustrup’s C++ FAQ]
    Federico Ficarelli, Idiomatic C++ 38

    View full-size slide

  39. Constructors: let them fail
    Warning: the constructor itself must be
    exception-safe (without relying on destructor).
    class T {
    public:
    T(std::size_t len = 0) :
    array( new int[len] ),
    buffer( new char[len]) {} // <-- ?
    // Destructor (omitted)
    private:
    int* array;
    char* buffer;
    };
    class T {
    public:
    T(std::size_t len = 0) :
    array( new int[len] ),
    buffer( new char[len]) {} // <-- ?
    // Destructor (omitted)
    private:
    std::shared_array array;
    std::shared_array buffer;
    };
    Federico Ficarelli, Idiomatic C++ 39

    View full-size slide

  40. Resources: the Gold Rule
    class dumb_string {
    public:
    dumb_string(std::size_t size = 0) : // conversion/default
    mSize(size),
    mArray(mSize ? new char[mSize]() : 0) {}
    dumb_string(const dumb_string& other) : // copy
    mSize(other.mSize),
    mArray(mSize ? new char[mSize]() : 0) {
    std::copy(other.mArray, other.mArray + mSize, mArray);
    }
    virtual ~dumb_string() { // destructor
    delete [] mArray;
    }
    private:
    std::size_t mSize;
    char* mArray;
    };
    int main() {
    dumb_string a(10);
    dumb_string b(a);
    dumb_string c;
    c = a; // ?
    }
    Rule Of Three:
    if you need to explicitly declare either the destructor, copy constructor or copy
    assignment operator yourself, you probably need to explicitly declare all three of
    them.
    Federico Ficarelli, Idiomatic C++ 40

    View full-size slide

  41. The easy way: non-copyable types
    #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
    TypeName(const TypeName&); \
    void operator=(const TypeName&)
    // ...
    private:
    DISALLOW_COPY_AND_ASSIGN(T);
    };
    template
    class NonCopyable
    {
    protected:
    NonCopyable () {}
    ~NonCopyable () {} // Protected non-virtual
    private:
    NonCopyable (const NonCopyable &);
    NonCopyable & operator = (const NonCopyable &);
    };
    class CantCopy : private NonCopyable {};
    CRTP Mixin: enables Empty Base Optimization
    Disable Copying:
    whenever it makes sense, explicitly disable copy by construction and by
    assignment. This prevents the language from treating types with
    unwanted/erratic value semantics.
    Federico Ficarelli, Idiomatic C++ 41

    View full-size slide

  42. Copy-assignment operator: the tough
    spot
    ◼ We need to implement the copy-assignment
    operator in order to have a correct value
    semantics;
    ◼ operator= is much more hard to implement
    in a robust way than the copy constructor:
    ▪ must handle an already constructed object;
    ▪ in case of failure, it must leave the object in the
    previous consistent state (rollback).
    The copy-assignment operator must be transactional.
    Federico Ficarelli, Idiomatic C++ 42

    View full-size slide

  43. operator=: first attempt
    Issues:
    ◼ self assignment test: a symptom of non-robust
    implementation; usually very rare (performance
    waste);
    ◼ non exception-safe.
    dumb_string& operator=(const dumb_string& other)
    {
    if (this != &other) // <--
    {
    // Tear down object’s state…
    delete [] mArray; // <--
    mArray = 0; // avoid double-deletion in case of RAII
    // ...and setup the new one...
    mSize = other.mSize; // <--
    mArray = mSize ? new int[mSize] : 0;
    std::copy(other.mArray, other.mArray + mSize, mArray); // <--
    }
    return *this;
    }
    Federico Ficarelli, Idiomatic C++ 43

    View full-size slide

  44. operator=: exception safety
    dumb_string& operator=(const dumb_string& other)
    {
    if (this != &pOther) // <--
    {
    // setup the new data ready before we teardown the old
    std::size_t newSize = other.mSize;
    int* newArray = newSize ? new int[newSize]() : 0; // <--
    std::copy(other.mArray, other.mArray + newSize, newArray);
    // replace the old data (all are nothrow)
    delete [] mArray;
    mSize = newSize;
    mArray = newArray;
    }
    return *this;
    }
    Issues:
    ◼ code duplication.
    Federico Ficarelli, Idiomatic C++ 44

    View full-size slide

  45. operator=: Copy-and-swap Idiom
    friend void swap(dumb_string& first, dumb_string& second) throw()
    {
    std::swap(first.mSize, second.mSize); // throw()
    std::swap(first.mArray, second.mArray); // throw()
    }
    dumb_string& operator=(const dumb_string& other)
    {
    dumb_array temp(other);
    swap(*this, temp); // <--
    return *this;
    }
    dumb_string& operator=(dumb_string other) // <--
    {
    swap(*this, other); // <--
    return *this;
    }
    Pass by Value: enables Copy Elision Optimization
    ◼ Enables strong exception-guarantee (especially the RVO
    version);
    ◼ enables type to be used with a large number of idioms.
    Rule-of-Three-and-a-half:
    whenever it makes sense, provide a no-fail swap.
    Federico Ficarelli, Idiomatic C++ 45

    View full-size slide

  46. Transactional programming: essential
    assumption
    Foundations Never Fail:
    everything that destructor, deallocation (e.g.: operator delete)
    and swap functions attempt shall succeed: never allow an error to be
    reported from within them. They are the foundation of transactional
    programming: without their resilience, no-fail rollback is impossible to
    implement.
    Federico Ficarelli, Idiomatic C++ 46

    View full-size slide

  47. Copy and destroy consistently
    When writing a class, consider:
    ◼ Rule-of-three(and-a-half) to obtain correct
    and robust value semantics for complex types;
    ◼ «None-of-three» for POD/aggregates (let the
    compiler generate them for you);
    ◼ explicitly disable copy-construction and copy
    assignment.
    Federico Ficarelli, Idiomatic C++ 47

    View full-size slide

  48. Chapter 3
    Namespaces

    View full-size slide

  49. Keep interfaces coherent
    ◼ The language is explicitly designed to enforce
    the Interface Principle, ADL/Koenig Lookup
    was added for this reason.
    Interface Principle:
    for a class T, all functions (including non-member) that both "mention" T
    and are "supplied with" T in the same namespace are logically part of T, because
    they form part of T's interface.
    Federico Ficarelli, Idiomatic C++ 49

    View full-size slide

  50. Argument Dependent/Koenig Name
    Lookup
    ◼ Only occurs if the normal lookup of an unqualified
    name fails to find a matching class member function.
    “The set of declarations [...] considered for resolution of the
    function name is the union of the declarations found by normal
    lookup with the declarations found by looking in the set of
    namespaces associated with the types of the function
    arguments”. [C++03, 3.4.2]
    // using namespace std;
    std::cout << "hello" << std::endl;
    Federico Ficarelli, Idiomatic C++ 50

    View full-size slide

  51. Keep interfaces coherent
    namespace type {
    class T {
    public:
    void f();
    };
    }
    namespace ops {
    T operator+( const T&, const T& );
    }
    int main() {
    // using ops::operator+;
    type::T a, b;
    a.f();
    type::T c = ops::operator+(a, b);
    }
    namespace ns {
    class T {
    public:
    void f();
    };
    T operator+( const T&, const T& );
    }
    int main() {
    ns::T a, b;
    a.f();
    ns::T c = a + b;
    }
    Interface Principle, corollary:
    keep a type and its non-member function interfaces in the same namespace.
    Federico Ficarelli, Idiomatic C++ 51

    View full-size slide

  52. Keep interfaces clean
    Depending on std implementation, ADL may
    choose:
    ◼ std::operator+
    ◼ N::operator+ (pulled in the name search by
    vector)
    #include
    namespace N {
    struct X {};
    template int* operator+( T, unsigned )
    { /* do something */ }
    }
    int main() {
    std::vector v(5);
    v[0]; // <-- v.begin() + 0
    }
    Federico Ficarelli, Idiomatic C++ 52

    View full-size slide

  53. Keep interfaces clean
    Help prevent name-lookup accidents: protect
    types from unwanted ADL.
    «Dual» Interface Principle:
    avoid putting non-member functions that are not part of the interface of
    a type T into the same namespace as T, and especially never put
    templated functions or operators into the same namespace as a user-
    defined type.
    Federico Ficarelli, Idiomatic C++ 53

    View full-size slide

  54. Avoid namespace pollution
    Beware: B::g resolution (and
    semantics) depends on the
    headers inclusion order.
    // f1.h
    namespace A {
    int f(double);
    }
    // g.h
    namespace B {
    using A::f;
    void g();
    }
    // f2.h
    namespace A {
    int f(int);
    }
    // g.cpp
    B::g() {
    f(1); // <-- ?
    }
    Namespace Using Principle:
    avoid putting using namespace directives before any inclusion
    directive. Since the inclusion ordering is out of implementor’s control and
    depends on client’s implementation, never put using namespace
    directives inside header files.
    Federico Ficarelli, Idiomatic C++ 54

    View full-size slide

  55. Appendix
    Tools

    View full-size slide

  56. Static analysis tools
    ◼ Clang Static Analyzer
    ◼ Mozilla dehydra
    ◼ vera++
    ◼ cppcheck
    ◼ Google cpplint
    Federico Ficarelli, Idiomatic C++ 56

    View full-size slide