QuantStack

Polymorphism

Update the repo

  • git checkout master
  • git pull upstream master
  • git push origin master
  • git checkout -b types

Inheritance


    // In volatility.hpp
    class volatility
    {
    public:

        std::string underlying_name() const;
    };

    class implied_volatility : public volatility
    {
    };
                        

Inheritance


    // In volatility.cpp
    std::string volatility::underlying_name() const
    {
        return "BNP Paribas";
    }
                        

    // In main.cpp
    void test_access()
    {
        implied_volatility v;
        std::cout << v.underlying_name() << std::endl;
    }
                        

Inheritance

Changes to the volatility class:

  • Add a constructor taking the name of the underlying
  • Add a data member for storing the name of the underlying
  • Change the underlying_name method accordingly

Inheritance


    // volatility.hpp
    class volatility
    {
    public:

        explicit volatility(const std::string& name);

        const std::string& underlying_name() const;

    private:

        std::string m_name;
    };
                        

Inheritance


    // volatility.hpp
    class volatility
    {
    public:

        explicit volatility(const std::string& name = "");

        const std::string& underlying_name() const;

    private:

        std::string m_name;
    };
                        

Inheritance


    // volatility.cpp
    volatility::volatility(const std::string& name)
        : m_name(name)
    {
    }
                        

Inheritance


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        explicit implied_volatility(const std::string& name = "");
    };
                        

    // volatility.cpp
    implied_volatility::implied_volatility(const std::string& name)
        : volatility(name)
    {
    }
                        

Inheritance

  • Add an integer id data member in volatility
  • Add a print method in implied_volatility that prints this id

Inheritance


    // volatility.hpp
    class volatility
    {
    public:

        explicit volatility(const std::string& name = "");
        const std::string& underlying_name() const;

    private:

        std::string m_name;
        int m_id;
    };
                        

Inheritance


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        explicit implied_volatility(const std::string& name = "");
        void print() const;
    };

    // volatility.cpp
    void implied_volatility::print() const
    {
        std::cout << "implied volatility " << m_id << std::endl;
    }
                        

Inheritance


    // volatility.hpp
    class volatility
    {
    public:

        explicit volatility(const std::string& name = "");
        const std::string& underlying_name() const;

    protected:

        int m_id;

    private:

        std::string m_name;
    };
                        

Inheritance

  • public: access granted to everyone
  • private: access granted to class only
  • protected: access granted to class and inheriting classes

Avoid putting data members in the protected section, prefer methods

Polymorphism


    // main.cpp
    void test_polymorphism(const volatility& vol)
    {
        std::cout << vol.underlying_name() << std::endl;
    }

    void test_polymorphism()
    {
        implied_volatility iv("JP Morgan");
        test_polymorphism(iv);
    }
                        

Polymorphism

  • Add a m_volatility member in implied_volatility (std::vector<double>)
  • Modify the constructor accordingly
  • Add a double get_volatility(size_t index) method in implied_volatility
  • Add the same method in volatility (should return 0.)
  • Make get_volatility method print the name of the class before returning
  • In the test_polymorhism method, print the result of get_volatility for both types

Polymorphism


    // volatility.hpp
    class volatility
    {
    public:

        double get_volatility(size_t index) const;
    };
                        

Polymorphism


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        implied_volatility(const std::string& name, const std::vector<double>& vol);

        double get_volatility(size_t index) const;

    private:

        std::vector<double> m_volatility;
    };
                        

Polymorphism


    // volatility.cpp
    double volatility::get_volatility(size_t index) const
    {
        std::cout << "volatility::get_volatility" << std::endl;
        return 0.;
    }

    implied_volatility::implied_volatility(const std::string& name,
                                           std::vector<double>& vol)
        : volatility(name), m_volatility(vol)
    {
    }

    double implied_volatility::get_volatility(size_t index) const
    {
        std::cout << "implied_volatility::get_volatility" << std::endl;
        return m_volatility[index];
    } 
                        

Polymorphism


    // main.cpp
    void test_polymorphism(const volatility& vol)
    {
        std::cout << vol.underlying_name() << std::endl;
        std::cout << vol.get_volatility(0) << std::endl;
    }

    void test_polymorphism()
    {
        volatility v("JP Morgan")
        test_polymorphism(v);
        implied_volatility iv("JP Morgan", { 0.15, 0.20, 0.25, 0.26});
        test_polymorphism(iv);
    }
                        

Polymorphism


    // volatility.hpp
    class volatility
    {
    public:

        virtual double get_volatility(size_t index) const;
    };
                        

Polymorphism

virtual

  • is not part of the signature
  • implies each overload in inheriting classes is virtual too
  • does not prevent to declare such overloads as virtual
  • does not force inheriting classes to declare overloads

Polymorphism


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        implied_volatility(const std::string& name, const std::vector<double>& vol);

        virtual double get_volatility(size_t index) const;

    private:

        std::vector<double> m_volatility;
    };
                        

Polymorphism


    // volatility.hpp
    class volatility
    {
    public:

        virtual double get_volatility(size_t index) const = 0;
    };

    // main.cpp
    void test_polymorphism()
    {
        //volatility v("JP Morgan")
        //test_polymorphism(v);
        implied_volatility iv("JP Morgan", { 0.15, 0.20, 0.25, 0.26});
        test_polymorphism(iv);
    }
                        

Polymorphism


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        implied_volatility(const std::string& name, const std::vector<double>& vol);

        // Defines a new virtual method, not defined in the base class
        // Becomes an abstract class since it does not implement
        // get_volatility(size_t index) virtual method
        virtual double get_volatility(size_t index, double date) const;

    private:

        std::vector<double> m_volatility;
    };
                        

Polymorphism


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        implied_volatility(const std::string& name, const std::vector<double>& vol);

        // Ok, implements abstract method defined in base class
        double get_volatility(size_t index) const override;
        // Error, no get_volatility(size_t, double) declared in base class
        double get_volatility(size_t index, double date) const override;

    private:

        std::vector<double> m_volatility;
    };
                        

Polymorphism

  • is not part of the signature
  • Requires the method to be declared in the base class with the same signature

Entity semantic

  • Add a log in the constructor of volatility classes
  • Add destructors (and a log inside them) to the volatility classes
  • In test, replace stack-allocated volatility with dynamic-allocated one
  • Dynamic-allocated volatility should be stored as volatility*

Entity semantic


    // volatility.hpp
    class volatility
    {
    public:

        ~volatility();
    };

    class implied_volatility : public volatility
    {
    public:

        ~implied_volatility();
    };
                        

Entity semantic


    volatility::~volatility()
    {
        std::cout << "volatility destructor" << std::endl;
    }

    implied_volatility::~implied_volatility()
    {
        std::cout << "implied_volatility destructor" << std::endl;
    }
                        

Entity semantic


    // volatility.cpp
    volatility::volatility(const std::string& name)
        : m_name(name)
    {
        std::cout << "volatility_constructor" << std::endl;
    }

    implied_volatility::implied_volatility(const std::string& name,
                                           std::vector<double>& vol)
        : volatility(name), m_volatility(vol)
    {
        std::cout << "implied_volatility_constructor" << std::endl;
    }
                        

Entity semantic


    // main.cpp
    void test_polymorphism()
    {
        volatility* iv = new implied_volatility("JP Morgan", { 0.15, 0.20, 0.25, 0.26});
        test_polymorphism(*iv);
        delete iv;
    }
                        

Entity semantic


    // volatility.hpp
    class volatility
    {
    public:

        virtual ~volatility();
    };
                        

Entity semantic

Add a bumped_volatility class

  • It inherits from volatility
  • Its constructor takes a volatility* and a double parameters
  • Its get_volatility method returns the bumped volatility
  • The constructor and destructor print the name of the class
  • It does not own the bumped volatilty

Entity semantic


    // volatility.hpp
    class bumped_volatility : public volatility
    {
    public:

        bumped_volatility(volatility* vol, double bump);
        virtual ~bumped_volatility();

        virtual double get_volatility(size_t index) const;

    private:

        volatility* p_volatility;
        double m_bump;
    };
                        

Entity semantic


    // volatility.cpp
    bumped_volatility::bumped_volatility(volatility* vol, double bump)
        : volatility(vol->underlying_name()), p_volatility(vol, m_bump(bump)
    {
        sd::cout << "bumped_volatility constructor" << std::endl;
    }

    bumped_volatility::~bumped_volatility()
    {
        sd::cout << "bumped_volatility destructor" << std::endl;
        p_volatility = nullptr;
    }
                        

Entity semantic


    // volatility.cpp
    double bumped_volatility::get_volatility(size_t index) const
    {
        return p_volatility->get_volatility(index) + m_bump;
    }

    //main.cpp
    void test_polymorphism()
    {
        implied_volatility* iv = new implied_volatility("JP Morgan", { 0.15, 0.20, 0.25, 0.26});
        test_polymorphism(*iv);
        bumped_volatility* bv = new bumped_volatility(iv, 0.01);
        test_polymorphism(*bv);
        delete bv;
        delete iv;
    }
                        

Entity semantic

Add make_volatility methods


    volatility* make_volatility(const std::string& ud, const std::vector<double>& vol);
    volatility* make_volatility(volatility* vol, double bump);
                        

    volatility* make_volatility(const std::string& ud, const std::vector<double>& vol)
    {
        return new implied_volatility(ud, vol);
    }
    volatility* make_volatility(volatility* vol, double bump)
    {
        return new bumped_volatility(vol, bump);
    }
                        

Entity semantic


    //main.cpp
    void test_polymorphism()
    {
        volatility* iv = make_volatility("JP Morgan", { 0.15, 0.20, 0.25, 0.26});
        test_polymorphism(*iv);
        volatility* bv = make_volatility(iv, 0.01);
        test_polymorphism(*bv);
        delete bv;
        delete iv;
    }
                        

Entity semantic

Add a print method to the volatility classes, which prints each data member


    // volatility.hpp
    class volatility
    {
    public:

        virtual void print() const;
    };
    // Same declaration in implied_volatility
    // and bumped_volatility classes
                        

Entity semantic


    // volatility.cpp
    void volatility::print() const
    {
        std::cout << "underlying: " << underlying_name() << std::endl;
    }

    void implied_volatility::print() const
    {
        volatility::print();
        std::cout << "volatility: (";
        std:copy(m_volatility.begin(), m_volatility.end(), std::ostream_iterator<double>(std::cout, ", "));
        std::cout << ")" << std::endl;
    }
                        

Entity semantic


    // volatility.cpp
    void bumped_volatility::print() const
    {
        std::cout << "initial volatility: " << std::endl;
        p_volatility->print();
        std::cout << "bump: " << m_bump << std::endl;
    }
                        

Entity semantic

In main.cpp creates a test_assign function that:

  • Creates two instances of implied_volatility via make_volatility
  • Assigns the second one to the first one
  • Prints both instances
  • Creates an instance of bumped volatility via make_volatility
  • Assigns it to one of the implied_volatility object
  • Prints the result

Entity semantic


    // main.cpp
    void test_assign()
    {
        volatility* iv1 = make_volatility("BNP Paribas", { 0.15, 0.20, 0.21, 0.22});
        volatility* iv2 = make_volatility("JP Morgan", { 0.12, 0.17, 0.20});
        *iv1 = *iv2;
        iv1->print();
        iv2->print();
        volatility* bv = make_volatility(iv2, 0.01);
        *iv2 = *bv;
        iv2->print();
        delete bv;
        delete iv2;
        delete iv1;
    }
                        

Entity semantic

  • Incomplete assignment due to inheritance is called slicing
  • Constructors and assign operators cannot be virtual

Entity semantic


    class volatility
    {
    public:

        volatility(const std::string& name);
        virtual ~volatility();
        volatility(const volatility& rhs);
        volatility& operator=(const volatlity& rhs);

        virtual void print() const;
    };
                        

DONT. DO. THAT.

Entity semantic

Prevent users to call copy constructor / assign operator


    class volatility
    {
    public:

        volatility(const std::string& name);
        virtual ~volatility();

    private:

        volatility(const volatility& rhs);
        volatility& operator=(const volatility& rhs);
        volatility(volatility&& rhs);
        volatility& operator=(volatility&& rhs);
    };
                        

Entity semantic

Prevent users to call copy constructor / assign operator


    class volatility
    {
    public:

        volatility(const std::string& name);
        virtual ~volatility();

        volatility(const volatility& rhs) = delete;
        volatility& operator=(const volatility& rhs) = delete;
        volatility(volatility&& rhs) = delete;
        volatility& operator=(volatility&& rhs) = delete;
    };
                        

Entity semantic

Prevent instantiation of base class


    class volatility
    {
    public:

        virtual ~volatility();

        volatility(const volatility& rhs) = delete;
        volatility& operator=(const volatility& rhs) = delete;
        volatility(volatility&& rhs) = delete;
        volatility& operator=(volatility&& rhs) = delete;

    protected:

        volatility(const std::string& name);
    };
                        

Entity semantic

How to copy an instance of implied_volatility ?


    // volatility.hpp
    class volatility
    {
    public:

        virtual ~volatility();
        virtual volatility* clone() const = 0;
    };
                        

Entity semantic


    // volatility.hpp
    class implied_volatility : public volatility
    {
    public:

        virtual ~implied_volatility();
        virtual volatility* clone() const;
    }
                        

Entity semantic


    // volatility.cpp
    volatility* implied_volatility::clone() const
    {
        implied_volatility* res = new implied_volatility(underlying_name(), m_volatility);
        return res;
    }
                        

    // volatility.cpp
    volatility* implied_volatility::clone() const
    {
        // Actually needs the copy constructor!
        return new implied_volatility(*this);
    }
                        

Entity semantic


    class volatility
    {
    public:

        virtual ~volatility();
        virtual volatility* clone() const = 0;

        volatility& operator=(const volatility& rhs) = delete;
        volatility(volatility&& rhs) = delete;
        volatility& operator=(volatility&& rhs) = delete;

    protected:

        volatility(const std::string& name);
        volatility(const volatility& rhs);
    };
                        

Entity semantic


    class implied_volatility
    {
    public:

        implied_volatility(const std::string& name, const std::vector<double>& vol);
        virtual ~implied_volatility();
        virtual implied_volatility* clone() const;

    protected:

        implied_volatility(const implied_volatility& rhs);
    };
                        

Entity semantic


    volatility::volatility(const volatility& rhs)
        : m_name(rhs.m_name)
    {
    }

    implied_volatility::implied_volatility(const implied_volatility& rhs)
        : volatility(rhs), m_volatility(rhs.m_volatility)
    {
    }
                        

Entity semantic


    void test_cast()
    {
        volatility* v1 = make_volatility("BNP Paribas", {0.15, 0.18, 0.20, 0.22});
        implied_volatility* iv = dynamic_cast<implied_volatility*>(v1);
        bumped_volatility* bv = dynamic_cast<bumped_volatility*>(v1);
        std::cout << iv << std::endl;
        std::cout << bv << std::endl;
        delete v1;
    }
                        

Design principles

  • B should inherit from A if B is a kind of A
  • B should inherit from A if there exists an "is-a" relation between A and B

That's WRONG

  • B should inherit from A if B behaves like A in the context of your program
  • B should inherit from A if you want A to be substitutable by B

Design principles

Liskov Substitution Principle

If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of the program

Design principles


    class rectangle : public drawable
    {
    public:

        virtual ~rectangle();
        virtual void draw(const canvas& c) const;

        virtual void resize(int length, int width);
    };
                        

Should square inherit from rectangle?

Design principles


    class square : public rectangle
    {
    public:

        virtual ~square();
        virtual void draw(const canvas& c) const;

        virtual void resize(int length, int width); // ???
    };
                        

square should NOT inherit from rectangle

Design principles

Non-virtual interface


    class volatility
    {
    public:

        double get_volatility(size_t index) const;

    private:

        virtual double get_volatility_impl(size_t index) const = 0;
    };
                        

Design principles

Non-virtual interface


    double volatility::get_volatility(size_t index) const
    {
        // add code that should be executed before any call to get_volatility_impl
        // that may be assert on the inputs
        double res = get_volatility_impl(index);
        // add code that should be executed after any call to get_volatility_impl
        return res;
    }
                        

Design principles

Template method


    class algo
    {
    public:

        void run();

    protected:

        virtual void first_step();
        virtual void second_step();
        virtual void third_step();
    }
                        

Design principles

Template method


    void algo::run()
    {
        first_step();
        second_step();
        third_step();
    }
                        

Design principles


    class my_vector : public vector
    {
    public:

        explicit my_vector(const std::string& name);
        ~my_vector();

    private:

        std::string m_name;
    };
                        

DONT. DO. THAT.

Private inheritance

Design a named vector


    class my_vector
    {
    public:

        double& operator[](size_t i);
        const double& operator[](size_t i) const;

    private:

        std::vector<double> m_data;
        std::string m_name;
    };
                        

Private inheritance


    class my_vector : private std::vector<double>
    {
    public:

        using base_type = std::vector<double>;
        
        using base_type::size;
        using base_type::empty;
        using base_type::operator[];

    private:

        std::string m_data;
    };
                        

Private inheritance

Assume B is a private base of D

  • All methods of B are private in D
  • You cannot assign a D* to a B*
  • Previous statements are not true for friend classes / functions
  • D can only acces public and protected sections of B

Multiple inheritance


    class implementation : public interface1, public interface2
    {
    public:

        virtual ~implementation();
    };
                        

Multiple inheritance


    class root {};
    
    class interface_1 : public root {};
    class interface_2 : public root {};

    // The following is problematic
    class implementation : public interface_1, public interface2 {};
                        

Multiple inheritance


    class implied_volatility : public volatility, private surface
    {
    public:

        ~implied_volatility();
    };