@SylvainCorlay @JohanMabille @QuantStack
Update the repo
// In volatility.hpp
class volatility
{
public:
std::string underlying_name() const;
};
class implied_volatility : public volatility
{
};
// 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;
}
Changes to the volatility class:
// volatility.hpp
class volatility
{
public:
explicit volatility(const std::string& name);
const std::string& underlying_name() const;
private:
std::string m_name;
};
// volatility.hpp
class volatility
{
public:
explicit volatility(const std::string& name = "");
const std::string& underlying_name() const;
private:
std::string m_name;
};
// volatility.cpp
volatility::volatility(const std::string& name)
: m_name(name)
{
}
// 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)
{
}
// 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;
};
// 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;
}
// 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;
};
Avoid putting data members in the protected section, prefer methods
// 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);
}
// volatility.hpp
class volatility
{
public:
double get_volatility(size_t index) const;
};
// 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;
};
// 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];
}
// 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);
}
// volatility.hpp
class volatility
{
public:
virtual double get_volatility(size_t index) const;
};
virtual
// 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;
};
// 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);
}
// 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;
};
// 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;
};
// volatility.hpp
class volatility
{
public:
~volatility();
};
class implied_volatility : public volatility
{
public:
~implied_volatility();
};
volatility::~volatility()
{
std::cout << "volatility destructor" << std::endl;
}
implied_volatility::~implied_volatility()
{
std::cout << "implied_volatility destructor" << std::endl;
}
// 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;
}
// 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;
}
// volatility.hpp
class volatility
{
public:
virtual ~volatility();
};
Add a bumped_volatility class
// 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;
};
// 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;
}
// 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;
}
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);
}
//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;
}
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
// 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;
}
// volatility.cpp
void bumped_volatility::print() const
{
std::cout << "initial volatility: " << std::endl;
p_volatility->print();
std::cout << "bump: " << m_bump << std::endl;
}
In main.cpp creates a test_assign function that:
// 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;
}
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.
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);
};
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;
};
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);
};
How to copy an instance of implied_volatility ?
// volatility.hpp
class volatility
{
public:
virtual ~volatility();
virtual volatility* clone() const = 0;
};
// volatility.hpp
class implied_volatility : public volatility
{
public:
virtual ~implied_volatility();
virtual volatility* clone() const;
}
// 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);
}
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);
};
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);
};
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)
{
}
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;
}
That's WRONG
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
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?
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
Non-virtual interface
class volatility
{
public:
double get_volatility(size_t index) const;
private:
virtual double get_volatility_impl(size_t index) const = 0;
};
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;
}
Template method
class algo
{
public:
void run();
protected:
virtual void first_step();
virtual void second_step();
virtual void third_step();
}
Template method
void algo::run()
{
first_step();
second_step();
third_step();
}
class my_vector : public vector
{
public:
explicit my_vector(const std::string& name);
~my_vector();
private:
std::string m_name;
};
DONT. DO. THAT.
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;
};
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;
};
Assume B is a private base of D
class implementation : public interface1, public interface2
{
public:
virtual ~implementation();
};
class root {};
class interface_1 : public root {};
class interface_2 : public root {};
// The following is problematic
class implementation : public interface_1, public interface2 {};
class implied_volatility : public volatility, private surface
{
public:
~implied_volatility();
};