Introduction to C++ for Financial Engineers-Study Notes 1(Ch1-Ch7)

1.编程范式 programming paradigm

包括OO,modular paradigm

OO是面向对象编程,回顾与之相关的重要概念,类,继承,封装,数据抽象和动态绑定。

而module不同于member function,不需要去创造一个对象来使用它。

类似于泛型算法。就比如C++ stl的顺序容器并没有过度使用面向对象,而是通过一组算法库来实现对所有顺序容器和数组的操作。

又比如之后遇到的,对于利率相关的函数,比如现值,年金,终值我们不是通过构造类和OO,而是通过把它们放在一个namespace中。


2.头文件用来连接名字的声明declaration和使用,对于头文件的使用有一些细节问题,头文件一般是函数的声明,即函数原型,而函数的具体定义包含在相应的源文件中。同样地,类的定义在头文件中,类的成员函数的声明也在头文件中,但是类的成员函数一般具体定义在code file中,这时code file需要include头文件。

头文件与相应的源文件都要注意使用预处理器来防止重复包含头文件比如

#ifndef SALES_ITEM_H

#ifndef SALES_ITEM_CPP

在包含头文件时要注意,头文件一般来自于三个地方

D1: System directories

D2: Remote (‘Other’) directories

D3: Local directories

但是如果是D2,D3,那么相应的code file也要在当前目录下,并且包含时是包含头文件而不需要包含code file

// Person.hpp
// "Hello World" class. Function declarations.
// (C) Datasim Education BV 2005-2006
//
#ifndef Person HPP
#define Person HPP
#include "datasimdate.hpp" // My dates and other useful stuff
#include <string> // Standard string class in C++

using namespace std;
class Person
{
public: // Everything public, for convenience only
// Data
string nam; // Name of person
DatasimDate dob; // Date of birth
DatasimDate createdD; // When object was created
public:
Person (const string& name,const DatasimDate& DateofBirth);
void print() const;
int age() const;
};

#endif

The body of these functions is given in the .cpp file:
// Person.cpp
// "Hello World" class
// Last Modification Dates
// 2006-2-17 DD Kick-off
// (C) Datasim Education BV 2005-2006
#include "Person.hpp"
Person::Person (const string& name, const DatasimDate& DateofBirth)
{
nam = name;
dob = DateofBirth;
createdD = DatasimDate(); // default, today
}

void Person::print() const
{ // Who am I?
cout << "\ n** Person Data **\ n";
cout << "Name: " << nam << ", Date of birth: " << dob
<< ", Age: " << age() << endl;
}

int Person::age() const
{
return int( double(DatasimDate() - dob) / 365.0);
}

The test program is defined in the current directory and is given by:
// TestPerson.cpp
// "Hello World" Testing the first C++ class
// (C) Datasim Education BV 2005-2006
#include "datasimdate.hpp" // Dates and other useful stuff
#include "Person.hpp" // Interface functions for Person
#include <string> // Standard string class in C++
using namespace std;
int main()
{
DatasimDate myBirthday(29, 8, 1952);
string myName ("Daniel J. Duffy");
Person dd(myName, myBirthday);
dd.print();
DatasimDate bBirthday(06, 8, 1994);
string bName ("Brendan Duffy");
Person bd(bName, bBirthday);
bd.print();
return 0;
}

The output from this program is:
** Person Data **
Name: Daniel J. Duffy, Date of birth: 29/8/1952, Age: 53
** Person Data **
Name: Brendan Duffy, Date of birth: 6/8/1994, Age: 11


3.对于函数类和模板类,必须(include)包含code file而不是包含头文件

函数类的声明还是放在头文件中,而函数类的具体定义放在源文件中,这个具体定义的源文件也必须包含头文件,这与之前是非模板类也是一样的。头文件和源文件code file都要使用预处理器防止重复包含头文件。


4.Error的类型

Linkage errors occur because the bodies of functions or the initialisation of data cannot be

found. The root cause of these errors is usually:

E1: A typing error; for example, mismatch between the signature of a function as declared in

a header file and its definition in the code file

E2: You forgot to add the relevant source file to your project

An example of E1 would be in the person class where one of its member functions is declared

as:

int age(); // NO "const" in this declaration
while in the code file the function is defined as:
int Person::age() const
{
return int( double(DatasimDate() - dob) / 365.0);
}

Summarising, linker errors arise because the linker cannot find the code for a function that has

been used in some source file.


5.灵活地使用struct

Some general remarks on structs are:

They are useful as data containers  All the members are public (accessibility by client code is not an issue)  They can be used in conjunction with classes  In some applications they are used as a building block in interface technology (for example, the Component Object Model (COM))

struct可以用类似于数组的方式进行初始化。


6.在实际编程中,我们常常从数据库加载数据,这就需要把各种数据类型转成string,或者把string转成各种数据类型。这需要通过下面的过程实现。 1)Place the data type into a string stream object

2)Convert the string stream object to a string  

3)Return the new string object to the client code

The actual code is:
// Hard-coded example for starters
double myDouble = 1.0;
stringstream s;
s << myDouble;
string result = s.str();
cout << "String value is: " << result << endl;

实际中,我们把不同的类型转换为string,这时候可以通过函数模板来实现

template <typename T>
string getString(const T& value)

{
stringstream s;
s << value;
return s.str();
}

7.面向对象的例子,European  Option,header file, code file, source file。回顾如果要使用函数模板和类模板,必须包含code file, not header file。 在定义类的时候需要注意,把析构函数定义为虚函数,一般是安全的。
Failing to declare a destructor tobe virtual may result in memory problems, so we play safe.

8.有时候我们不用OO的编程范式,不需要通过创建对象来调用函数(成员函数),这时可以把相关的函数放入一个namespace。一个典型的例子是,我们要实现一些和利率相关的计算
Calculating the future value of a sum of money (paid once per year, m times a year and
continuous compounding)  
Future value of an ordinary annuity
Simple present value calculations  
Present value of a series of future values
Present value of an ordinary annuity
// SimpleBondPricing.hpp
//
// Simple functions for interest rate calcuations.
//
// (C) Datasim Education BV 2006
//
#ifndef SimpleBondPricing HPP
#define SimpleBondPricing HPP
#include <vector>
using namespace std;
namespace Chapter3CPPBook // Logical grouping of functions and others
{
// Handy shorthand synonyms
typedef vector<double> Vector;
// Recursive function to calculate power of a number. This
// function calls itself, either directly or indirectly
double power(double d, long n);
// Future value of a sum of money invested today
double FutureValue(double P0, long nPeriods, double r);
// Future value of a sum of money invested today, m periods
// per year. r is annual interest rate
double FutureValue(double P0, long nPeriods, double r, long m);
// Continuous compounding, i.e. limit as m -> INFINITY
double FutureValueContinuous(double P0, long nPeriods, double r);
// Future value of an ordinary annuity
double OrdinaryAnnuity(double A, long nPeriods, double r);
// Present Value
double PresentValue(double Pn, long nPeriods, double r);
// Present Value of a series of future values
double PresentValue(const Vector& prices,long nPeriods,double r);
// Present Value of an ordinary annuity
double PresentValueOrdinaryAnnuity(double A,long nPer,double r);
}
#endif

还有一个细节就是,如果我们需要的类型一般都是vector<double>类型,那么就可以使用typedef vector<double> vector。类似的一些细节还有,比如我们写函数模板或者类模板时,模板类型形参的名字最好仿照Haskell中的typeclass具有意义。比如我们要实现compare的函数模板,可以写成

template<typename Numeric>
Numeric compare(Numeric &A,Numeric &B){//}


9. Call by reference && Call by value 何时使用引用类型形参,三种情况,如果这时为了避免复制,那么要注意使用const.。常量成员函数是指不能改变调用该成员函数的对象。


10.静态数据成员和静态成员函数。C++中的几类特殊的内存。比如heap-based memory,这是程序运行时所占用的一块区域,自由存储区,涉及new,delete。automatic memory,这是自动对象在内存中存储的区域,随着函数的调用被创建和撤销。static memory,静态存储区域。包括四类变量:

全局变量和namespace中的变量

类的静态数据成员和成员函数

静态局部对象

回顾在之前提到静态变量时,我们就说了静态对象一般有两层含义,一是其为静态存储的,这就是指static memory,静态全局变量和全局变量都是静态存储的,区别在于前者是被当前文件访问,后者是被整个程序访问。而静态局部对象的核心在于其生命期同整个程序的生命期。


静态存储区域中的变量只可以被创建一次,且生命期与整个程序的生命期相同,在程序结束前都不会被撤销。

类的静态数据成员是类的一部分,而不是类的对象的一部分。

类的静态成员函数仅可以访问类的静态数据成员。

类的静态数据成员的初始化以及使用的例子。

class aclass{
public:
       static  int a;
};
int aclass::a=0; //初始化

void main(void)
{
int amain=0;
amain=aclass::a;//使用,无需定义相关类的变量而直接使用之。
}
再次强调类的静态对象是类的一部分,而不是类的对象的一部分。

1、静态数据成员在定义或说明时前面加关键字static。
2、静态成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式如下:
    <数据类型><类名>::<静态数据成员名>=<值>
  这表明:
  (1) 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。
  (2) 初始化时不加该成员的访问权限控制符private,public等。
  (3) 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。
3、静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化。
4、引用静态数据成员时,采用如下格式:
   <类名>::<静态成员名>
  如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。


11.提高performance的一些技巧:

1)内联函数,就类似于C语言的预处理器中宏调用与函数调用的区别。回顾预处理器是程序编译过程中的第一步。宏定义是将出现名字的地方都用替换文本进行替换,注意括号的使用,#define MAX(x,y) (X>Y)?(X):(Y)。在C语言中提高效率的做法还有使用register关键字,把变量存放在寄存器中。

2)use of anonymous object,This new specifier is a hint to the compiler that it should attempt to generate code when the
above member functions are called instead of laying down the code for the function once and then calling through the usual function call mechanism (Stroustrup, 1997). This may improve performance but this is not guaranteed.

比如:

Point Point::MidPoint(const Point& p2) const
{ // Calculate the point between the two points
Point result((x+p2.x)*0.5 , (y+p2.y)*0.5 );
return result;
}
Point Point::MidPoint(const Point& p2) const
{ // Calculate the point between the two points
// Create "any old" point
return Point( (x+p2.x)*0.5 , (y+p2.y)*0.5 );
}

3)关于循环的优化,要注意减少使用局部变量,并且避免矩阵和vector的复制


12.Chapter5 pp80.操作符重载, operator overloading in C++

在C++ PRIMER中已经指出有些重载操作符作为类的成员,有些则一般不作为。类的成员都有隐含的this指针作为形参,除了类的静态成员函数,static member function仅可以访问类的静态成员。

比如赋值操作符,复合赋值操作符一般重载为类的成员。而算术操作符一般重载为非类型成员,并将它们声明为友元,使其能够访问类的私有对象。

一些常见的使用操作符重载的情形

There are several applications of operator overloading that can be used in Financial Engineering:
Operations on Date, Time and TimeStamp objects (Duffy, 2004)  

Defining classes for algebraic data structures (Landin, 1969)  

Matrix algebra in C++ (Duffy, 2004)

Computer graphics (Foley et al., 1990)  

Expressions and expression parsers, for example composing mathematical equations at runtime

比如我们创建一个DatasimDate类型,要解决以下的问题

What is the date 100 days from now?

Compare two dates (is a date before another date?)  

How many days are there between two dates?  

Increment/decrement a date by one day Add a part of a year to a date, for example what is the date 6 months from now?

又比如我们要实现自定义Vector类型的点积运算,就可以重载^操作符

Vector operator ˆ (const Vector& vec) const;

回顾之前的常量成员函数,不能改变调用该函数的对象,这里重载操作符也可以视为特殊的成员函数,因此也可以声明为const

以复数的加法为例,这里还用到了之前说的增加效率的anonymous object技巧

Complex Complex::operator + (const Complex& c2) const
{ // Add two complex numbers
return Complex(x + c2.x, y + c2.y);
}

一个特殊的情形,赋值操作符的重载,assignment operator

Complex& Complex::operator = (const Complex& c)
{
// Avoid doing assign to myself
if (this == &c)
return *this;
x = p.x;
y = p.y;
return *this;
}
可以回顾一下之前提到的两类特殊的构造函数中,复制构造函数,复制构造函数除了隐含的this外只有一个形参,是对原本类型的引用。而重载赋值操作符也是。

输入和输出操作符的重载

返回类型分别是istream &和ostream &,形参为istream &,const CLASS &,ostream &,const CLASS &。也就是说重载输入或输出操作符不是类的成员,要将其声明为友元。

例子:

friend ostream& operator << (ostream& os, const Complex& cmp);
ostream& operator << (ostream& os, const Complex& cmp)
{ // Print the complex number
os << "(" << cmp.x << ", " << cmp.y << ")\ n";
return os;
}

也可以将一个类声明为友元,比如

class A
{
private: // Don’t tell others who my friends are
friend class B; // Hi B class, you my friend
// ...
};

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span>
13.C++中一些有用的类型,Array,Vector,Matrix,回顾类模板和函数模板,模板类型形参和模板非类型形参,以及模板类型推断和类型转换的问题。


14.回顾C++ primer中提到的类的用户分为两类,一是类的成员,而是类的使用者。回顾public,private,protected,如果不存在继承关系,那么只需要前两个访问标号即可,而protected中的成员可以通过派生类的对象来访问。而类的继承列表中,公有继承,私有继承和protected继承改变的只是用户对于派生类的成员的访问级别,而不是派生类对于基类的访问级别,也不改变用户对基类的访问。

15.回顾C中的命令行形参,可以从命令行输入参数传递给函数,main函数的两个形参分别是int argc,char *argv[],第一个表示从命令行输入的参数个数,第二个是指针数组,

且C语言规定,argv[0]是启动该程序的程序名,因此argc的值至少为1.第一个可选参数是argv[1],最后一个可选参数是argv[argc-1],且argv[argc]为空指针。


16.Chapter6 pp96 Memory Management 内存管理
有两类分配内存的方式,via the stack and via the heap

对于stack-based memory,要注意的就是变量在scope外是会自动撤消的,这与之前所说的不能返回局部对象的引用或者指针是一个意思。

int main()
{
// Define a scope
int j = 2;
cout << j << endl;
}
cout << j;
return 0;
}
这段代码是错的,因为变量j在块语句外已经被撤销了。This code will give a compiler error because the variable j does not exist outside the scope of
the block in which it is defined.

一般不是采用幻数+固定长度的数组,而是采用动态分配内存的方式。


动态变量与动态数组,heap-based memory,new delete的使用。

// Now create an array of options
SimpleOption* optArray;
//Now we define the array of options. Notice that the default constructor will be called ten times:
const int N = 10;
optArray = new SimpleOption[N]; // Default constructor called
//We now modify each option in the array:
for (int j = 0; j < N; j++)
{ // Member data public for convenience only
optArray[j].T = 1.0; // 1 year expiry
optArray[j].K = 100.0; // Strike price
optArray[j].print();
}
//Finally, we clean up the memory as follows (the square brackets are essential):
delete [] optArray;
回顾几种等价的表达,比如

int a[10];  //int *p=a; int *p=&a[0]; 

a[3]; //p[3];等价于*(p+3) 

动态数组的应用,一个例子,比如我们要建立复数的数组类,名为ComplexArray

class ComlexArray{
private:
	Complex* arr;
	int size;
public:	
	ComplexArray(int size);	
	ComplexArray(const ComplexArray& source);
};

The body of these constructors is given by

// Constructor with size
ComplexArray::ComplexArray(int Size)
{
arr=new Complex[size];
size=Size;
}
// Copy constructor
ComplexArray::ComplexArray(const ComplexArray& source)
{
// Deep copy source
size=source.size;
arr=new Complex[size];
for (int i=0; i<size; i++) arr[i]=source.arr[i];
} 
virtual ∼ComplexArray();
The body of the destructor is given by:
ComplexArray::∼ComplexArray()
{
delete[] arr;
}

17.Chapter7 Functions, Namespace, Inheritance

回顾const指针以及指向const对象的指针

int const* ptr

下文包含对函数指针的介绍以及namespace的介绍,A namespace is used to group logically related C++ code.比如之前提到的interest  rates相关的函数。要注意namespace中的变量也是静态存储的。回顾static memory,heap-based memory,stack-based memory要注意的问题(在scope外被撤销)

使用函数指针,并将它们作为类的成员,使用typedef简化函数指针类型的名称。

对于函数我们要知道,It must be possible to replace the code that implements a function by another block of code (this is called a Strategy pattern)

我们还要区分四类函数:

Scalar-valued function (maps a double to a double, for example)  

Vector function (maps a double into a vector)

Real-valued function (maps a vector into a double)  

Vector-valued function (maps a vector into a vector)

函数指针的一个例子,十分重要

void genericFunction (double myX, double myY,
double (*f) (double x, double y))
{
// Call the function f with arguments myX and myY
double result = (*f)(myX, myY);
cout << "Result is: " << result << endl;
}
在C++ Primer中提到,模板是泛型编程的基础,泛型编程是独立于特定类型的方式书写代码。而除了使用函数模板作用于不同类型的形参,而可以使用函数指针来实现泛型。比如泛型函数genericFunction,上面就是泛型函数的重要例子。

而泛型编程和OO都支持某种层面上的多态性polymorphism. 泛型编程是编译多态性。

int main()
{
double x = 3.0;
double y = 2.0;
genericFunction(x, y, add);
genericFunction(x, y, multiply);
genericFunction(x, y, subtract);
return 0;
}

18.Namespace 

A namespace is a mechanism for expressing logical grouping (Stroustrup, 1997). This mechanism
allows us to group code that belongs together into a common package as it were.

之前interest rates就是namespace的一个例子,我们再介绍如何使用namespace中的函数。两种基本的方法:

By means of qualified names  

The ‘using’ declaration  

The ‘using’ directive

具体而言 ,

namespace MyFunctions
{
double diffusion (double x) { return x; }
double convection (double x) { return x*x; }
}
namespace YourFunctions
{
double diffusion (double x) { return 2.0; }
double convection (double x) { return 1.0; }
}
cout << YourFunctions::convection (10.0) << endl;
using namespace MyFunctions;
cout << "Directive: \ n";
cout << convection (10.0) << endl;
cout << diffusion (2.0) << endl;


有的时候 namespace的名字太长,我们想简写它,或者说给它一个alies,就例如typedef一样,这时候可以使用

namespace YA = YourFunctions; // Define alias NS called YA
cout << YA::diffusion (2.0) << endl;


namespace与函数指针的配合使用,我们可以在一个namespace中使用函数指针,这样每个人都可以写自己函数版本的实现。

namespace StandardInterface
{
	// Namespace consisting of function pointers
	double (*func1) (double x);
	double (*func2) (double x, double y);
}

In general, different developers may have various implementations of these functions, for example:
namespace Implementation1
{
	double F1 (double x) { return x; }
	double F2 (double x, double y) { return x*y; }
}
namespace Implementation2
{
	double G1 (double x) { return -x; }
	double G2 (double x, double y) { return -x*y; }
}


Then we can switch (at compile time) between the different implementations just by assigning
each function pointer:
// Assign the function pointers from NS
StandardInterface::func1 = Implementation1::F1;
StandardInterface::func2 = Implementation1::F2;

We then can use the functions in a transparent way by using an alias:
using namespace StandardInterface;

cout << func1(2.0) << ", " << func2(3.0, -4.0) << endl;

Then, if we wish to use a different implementation of the namespace, we just assign the function pointers:

func1 = Implementation2::G1;
func2 = Implementation2::G2;
cout << func1(2.0) << ", " << func2(3.0, -4.0) << endl;

19.Inheritance Mechanism

回顾动态绑定的两个条件,一是虚函数,二是通过对基类的引用或者指针。派生类的实例也是基类的实例。只有在运行时,才知道绑定的对象的类型,这是运行多态性。继承层次的设计。

比如Person是基类,employer是派生类,employer多几个数据成员,比如salary, retiring_age,又比如要实现派生类版本的per_print函数。

在调用虚函数时可以传递对派生类对象的引用或指针,派生类实例本身也是基类的实例,这时候程序会匹配调用派生类的函数版本。

The use of virtual keyword in C++ allows us to implement polymorphism.


20.Multiple Inheritance多重继承

class D: public Base 1, public Base 2

 //回顾公有继承,私有继承,和protected继承,不改变派生类对基类的访问,改变的只是类的用户对于派生类成员的访问

private:

Base 2* base2;

public:

//Members here

};


21.非线性方程的解

f(x)=0 比如我们要求隐含波动率  C(sigma)-C_market=0

常见的方法:

1)Bisection Method

2)Newton's Method

3)Secant Method

4)Steffensen iteration

Bisection Method 二分法,假定f(x)在(a,b)之间有零解,f(a)f(b)<0。

Newton's Method :

X_{n+1}=X_{n}+h_{n}, h_{n}=-f(x_n)/f'(x_n)

Secant Method: 可以视为Newton方法的变形,但是需要两个初始值

X_{n+1}=X_{n}+h_{n}, h_{n}=-f_{n}*(x_{n}-x_{n-1})/(f_{n}-f_{n-1})

除了二分法外,其他方法都要设定初始值,且初始值对于方程能否找到收敛解十分重要。回顾隐含波动率的程序。


我们可以用OO的方法写NonlinearSolver,把NonlinearSolver作为父类,具体的求解方法作为派生类,比如Steffensen iteration

class NonlinearSolver{
public:
	double (*myF)(double x); //NonlinearSolver类有两个数据成员,一个是函数指针,用函数名来初始化这一变量。表示f(.)
	double tol; //预期的精度
public:
	NonlinearSolver(double (*function)(double)) {}//
	virtual double solve()=0;  //这是求值的成员函数,后面可以定义派生类的不同版本,比如牛顿法,Steffensen iteration方法。这里声明为纯虚函数,指出这个函数的版本没有意义,该函数的后代版本提供了可覆盖的接口
};


class SteffensenSolver: public NonlinearSolver{
private:
	double x0; //Initial Guess
	double xPrevious, xCurrent; 
	long n; //Number of iterations
public:
	SteffensenSolver(double guess,double (*myFunc)(double x))
	{
		x0=guess;
		xPrevious=x0;
		myF=myFunc;
	}
	double solve()
	{
		double tem;double hn; n=1; xPrevious=x0;
		temp=myF(xPrevious);
		hn=(myF(xPrevious+temp)-temp)/temp;
		hn=temp/hn;
		xCurrent=xPrevious-hn;
		xPrevious=xCurrent;
		n++;
		if(::fabs(hn)<=tol)
		{
			return xCurrent;
		}
		goto L1;
	}
	void printStatistics() const
	{
		cout<<"\n Data pertaining to Steffensen's method\n";
		cout<<"Value:"<<xCurrent<<endl;
		cout<<"Number of iterations (actual):"
			<<n<<endl;
	}
};

在求解隐含波动率时,我们只要先写函数C(sigma)-C_Market,再将函数名作为实参用来初始化一个Steffensen类对象,然后调用solve成员函数来求解。

double CallPrice(double sig)
{
	double S=59;
	double K=60;
	double r=0.067;
	double marketPrice=2.82;
	double b=r;
	double T=1;
	double temp=sig*sqrt(T);
	double d1=(log(S/K)+(b+(sig*sig)*0.5)*T)/temp;
	double d2=d1-temp;
	double calculatedValue=(S*exp((b-r)*T)*N(d1)-(K*(exp(-r*T)*N(d2));
	return marketPrice-calculatedValue;
}
double guess=0.2;
SteffensenSolver Steff(guess,CallPrice);
Steff.tol=0.0001;
double resultST=steff.solve();

这也是函数指针在面向对象和类当中的应用,再回顾函数指针与genericFunction。函数指针作为类的成员。比如方程的求解。随机微分方程也是类似的,函数指针类型的数据成员可以用不同函数来进行初始化。

如果我们不考虑精度的不同要求,也不考虑迭代次数,和初始值等问题,只要传递一个函数。那么我们不用先定义类,再定义类的成员函数solver,而是直接采用solver作为成员函数,但是前者的好处是可以利用动态绑定机制来实现多方法计算非线性方程的解。


22.Chapter 8 Advanced Inheritance and Payoff——Class Hierarchies

这一部分将涉及C++中继承机制的一些高级特点。

有关generalisation/specialisation 

PS:思考之前的非线性方程求解方法的继承层次设计。要注意什么时候时候面向对象和继承,回顾之前提到的不同的编程范式。是不是可以把不同的求解方法放在一个namespace里,辅助使用函数重载而不是用面向对象来实现。


一个设计好的继承层次式的我们拥有highly reusable and flexible software. 我们常常在base class中写invariant code,然后派生类继承这些invariant code。这称之为implementation inheritance,实现继承。另一种形式称之为Interface inheritance,接口继承。我们给出一个base class但是不提供任何的实现。pure virtual member functions纯虚函数的含义。

见下面的链接,接口继承与实现继承的区别

所谓接口继承,就是派生类只继承函数的接口,也就是声明;而实现继承,就是派生类同时继承函数的接口和实现。
1)声明一个纯虚函数(pure virtual)的目的是为了让派生类只继承函数接口,也就是上面说的接口继承。
2)声明非纯虚函数(impure virtual)的目的是让继承类继承该函数的接口和缺省实现。与纯虚函数唯一的不同就是其为继承类提供了缺省操作,继承类可以不实现自己的操作而采用基类提供的默认操作。
3)声明非虚函数(non-virtual)的目的是为了令继承类继承函数接口及一份强制性实现。相对于虚函数来说,非虚函数对继承类要求的更为严格,继承类不仅要继承函数接口,而且也要继承函数实现。也就是为继承类定义了一种行为。

---------

回顾malloc int(3); new double(3.0);函数的返回类型是void *


 具体类和抽象类 abstract and concrete class,抽象类不是抽象类型。后者对应的概念是数据抽象,依赖于接口和实现相分离的技术。

抽象类是为了抽象和设计的目的而建立的,处于继承层次结构的上层。不能实例化,抽象类是被具体类继承用的

具体类是能够建立对象的类。
抽象类的规定
(1)抽象类只能用作其他类的基类,不能建立抽象类对象。
(2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。
(3)可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。

http://blog.csdn.net/Slience_Perseverance/article/details/20536277

-----------------------------------------------------------------------------------------------------------------------------

插一句知乎上的回答:关于OO

没什么只有OO能做到,OO更多的是给了你一种能力,一种忽略细节的能力:忽略的越多,人类有限的智力就可以容纳越多越复杂的问题,并由此提高生产效率。
任何抽象的本质都是忽略,OO刚好是其中一种。

面向对象的三板斧分别是封装,继承和多态,他用封装将问题中的数据和对数据进行处理的函数结合在了一起,形成了一个整体的对象的概念,这样更加符合人的思维习惯,更利于理解,自然在理解和抽象一些复杂系统的时候也更加容易。他用继承来应对系统的扩展,在原有系统的基础上,只要简单继承,就可以完成系统的扩展,而无需重起炉灶。他用多态来应对需求的变化,统一的接口,却可以有不同的实现。

------------------------------------------------------------------------------------------------------------------------------

23.一个使用抽象类,具体类,接口继承和实现继承的例子。比如我们为了抽象和设计的目的,定义了一个名为Payoff的抽象类,该类没有任何的数据成员。这个好处是显然的,因为这样派生类就不需要继承多余的数据成员而只要定义各自的数据成员。此外,Payoff的虚函数我们将其声明为纯虚函数,这样派生类就启用接口继承,即只继承函数的接口,也就是声明,而不继承函数的实现。

回顾析构函数一般都声明为虚函数。

对于上面的抽象类Payoff,我们可以定义非常多的派生类来继承这个抽象类,比如Spreads,Straddle,Strangles这些交易策略都有payoff,而且每个派生类的成员都不同。继承层次的设计十分重要。一定要注意抽象类的使用。

代码如下:

class Payoff
{
public:
	// Constructors and destructor
	Payoff(); // Default constructor
	Payoff(const Payoff& source); // Copy constructor
	virtual ∼Payoff(); // Destructor
	// Operator overloading
	Payoff& operator = (const Payoff& source);
	// Pure virtual payoff function
	virtual double payoff(double S) const = 0; // Spot price S
};

class CallPayoff: public Payoff
{
private:
	double K; // Strike price
public:
	// Constructors and destructor
	CallPayoff();
	CallPayoff(double strike);
	CallPayoff(const CallPayoff& source);
	virtual ∼CallPayoff();
	// Selectors
	double Strike() const; // Return strike price
	// Modifiers
	void Strike(double NewStrike); // Set strike price
	CallPayoff& operator = (const CallPayoff& source);
	// Implement the pure virtual payoff function from base class
	double payoff(double S) const; // For a given spot price
};
CallPayoff::CallPayoff(const CallPayoff& source): Payoff(source)
{	 // Copy constructor
	K = source.K;
}
CallPayoff& CallPayoff::operator = (const CallPayoff &source)
{ 	// Assignment operator
	// Exit if same object
	if (this==&source) return *this;
	// Call base class assignment
	Payoff::operator = (source);
	// Copy state
	K = source.K;
	return *this;
}
double CallPayoff::Strike() const
{
	// Return K
	return K;
}
void CallPayoff::Strike(double NewStrike)
{	// Set K
	K = NewStrike;
}
double CallPayoff::payoff(double S) const
{ 	// For a given spot price
	if (S > K)
		return (S - K);
	return 0.0;
	// remark; possible to say max (S - K, 0)if you prefer
}


24.Lightweight Payoff classes

之前的Payoff classes中我们定义了一个纯虚函数并且利用接口继承,在派生类中实现了它。这么做是可行的,但是有很多的不足。

1.这样一来,不同的派生类都要定义一个新的payoff function。而不像非纯虚函数和实现继承那样,派生类可以继承该 函数的接口以及缺省实现。

2.我们一旦创造了一个实例就不能改变这个实例的类型。

我们可以采用另一种方法,见下图


我们定义一个Payoff的父类,这时该类不再是一个抽象类,Payoff类有一个指针类型的成员,

we create a single payoff class that has a pointer to what is essentially an encapsulation of a payoff function.

class Payoff
{
private:
	PayoffStrategy* ps;
public:
	// Constructors and destructor
	Payoff(PayoffStrategy& pstrat);
	// Other member functions
};


We see that we must give a reference to a payoff strategy. We have programmed the strategy classes in Figure 8.2 in one file as follows:

class PayoffStrategy
{
public:
	virtual double payoff(double S) const = 0;
};
//A specific derived class is given by:
class CallStrategy : public PayoffStrategy
{
private:
	double K;
public:
	CallStrategy(double strike) { K = strike;}
	double payoff(double S) const
	{
	if (S > K)
		return (S - K);
	return 0.0;
	}
};

这里PayoffStrategy也是声明了一个纯虚函数,而且是抽象类,而CallStrategy作为派生类继承了纯虚函数的接口,并且给出了自己实现的操作。但是不同就在于这时的Payoff不再是抽象类,而是包含指向基类的指针作为数据成员。

上面的方法被称为Strategy Pattern。这比之前的方法更加灵活和有效。这时候我们创建的PayoffStrategy类,本身没有给予payoff函数任何意义,PayoffStrategy也不对应于 任何折扣策略,它的存在只是为了让其他类继承。使一个函数作为纯虚函数就是指出这个函数版本没有意义,该函数的后代版本提供了可以覆盖的接口。


--------------------------------------------------------------------------------------

回顾C++ Primer中我们要写一个书的基类,并定义某种打折方式的派生类。如果我们有很多的打折方式,那么不一定是让各种打折方式的派生类来继承书的基类。我们可以采用一个更灵活的做法。让一个Disc_Item类继承Base_Item类,然后再让Bulk_Item继承Base_Item类,Disc_Item添加了新的数据成员,更好地适应了不同的打折方案。C++ Primer p492.这时重构的典型例子,重构包括重定义类的层次,将操作或数据从一个类移到另一个类。


26.Super lightweight payoff functions

再次重构,这时候我们要利用函数指针,函数指针在定义类时的用途是十分重要的。(此前,我们用函数指针实现了genericFunction,这是泛型的例子。用函数指针作为namespace中的变量,然后再利用作用域操作符定义该namespace中函数指针变量的具体版本。以及我们用函数指针作为类的数据成员来实现非线性函数求解,而具体的求解则作为NonliverSolver类的成员函数。)要注意NonlinearSolver类中solver函数也是纯虚函数,该函数仅仅是为了让派生类能够继承它的接口,各种数值方法的派生类给出了solve函数的不同版本。一定一定要灵活地使用纯虚函数。再比如Disc_Item中net_price()也是纯虚函数。

比如我们要写不同的期权定价方法,那么可以定义一个抽象类OptionSolver,该类仅包含一个纯虚函数option_pricing,再让不同的求解方法作为派生类继承这个类,然后定义自己的option_pricing版本。然后再定义一个类Option,Option的数据成员是指向OptinoSolver类型的指针。

下面介绍的实现payoff类和函数的方法是用函数指针来作为类的成员实现的,有点类似于之前的NonlinearSolver类的设计:

class OneFactorPayoff
{
private:
double K;
double (*payoffFN)(double K, double S);
public:
// Constructors and destructor
OneFactorPayoff(double strike,double(*payoff)(double K,double S));
// More ...
double payoff(double S) const; // For a given spot price
};
OneFactorPayoff::OneFactorPayoff(double strike,double (*pay)(double K, double S))
{
K = strike;
payoffFN = pay;
}
double OneFactorPayoff::payoff(double S) const
// For a given spot price
return payoffFN(K, S); // Call function
}


27.要注意派生类的复制构造函数一般显示地使用基类的复制构造函数初始化对象的基类部分。赋值操作符与复制构造函数类似,如果派生类定义了自己的赋值操作符,那么该操作符必须对基类部分进行显示赋值。

具体如下:

class Base{
public:
	Base(const Base &rhs);
private:
  ..//
};
class Bulk_Item: public Base{
	Bulk_Item(const Bulk_Item &d):
		Base(d) {}
};
Bulk_Item& Bulk_Item::operator=(const Bulk_Item &rhs){
	if(this!=&rhs)
		Base::operator=(rhs); //使用赋值操作符对基类部分进行显示赋值
}

26.C++中的struct与class很像,区别仅仅在于默认访问级别,并不同于C中的结构,回顾使用结构来实现统计输入的单词次数,struct tnode{}

STL的顺序容器都具有相应的适配器容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack适配器可使任何一种顺序容器以栈的方式工作。

27.模板函数也可以和非模板函数一样生命为inline,说明符放在形参表之后,返回类型之前

template<typename T> inline T Min(const T&,const T&)
从函数实参确定模板实参的类型和值得过程称之为 模板实参推断


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Practical C++ Financial Programming is a hands-on book for programmers wanting to apply C++ to programming problems in the financial industry. The book explains those aspects of the language that are more frequently used in writing financial software, including the STL, templates, and various numerical libraries. The book also describes many of the important problems in financial engineering that are part of the day-to-day work of financial programmers in large investment banks and hedge funds. The author has extensive experience in the New York City financial industry that is now distilled into this handy guide., Focus is on providing working solutions for common programming problems. Examples are plentiful and provide value in the form of ready-to-use solutions that you can immediately apply in your day-to-day work. You&rsquo;ll learn to design efficient, numerical classes for use in finance, as well as to use those classes provided by Boost and other libraries. You&rsquo;ll see examples of matrix manipulations, curve fitting, histogram generation, numerical integration, and differential equation analysis, and you&rsquo;ll learn how all these techniques can be applied to some of the most common areas of financial software development. These areas include performance price forecasting, optimizing investment portfolios, and more. The book style is quick and to-the-point, delivering a refreshing view of what one needs to master in order to thrive as a C++ programmer in the financial industry., Covers aspects of C++ especially relevant to financial programming. Provides working solutions to commonly-encountered problems in finance. Delivers in a refreshing and easy style with a strong focus on the practical., What you&rsquo;ll learn, Understand the fundamental problem types in the financial market. Design algorithms to solve financial programming problems. Extend C++ through Python extensions and LUA modules. Employ third-party numeric libraries such as those from Boost. Properly engage key C++ features such as templates and exception handling. Benefit from new features in C++14, such as auto variables and closures., Who this book is for, Practical C++ Financial Programming is for professionals or advanced students who have interest in learning C++ financial programming, especially in preparation for a professional career. Readers should have a working-knowledge of programming in C, C++, or some other C-like language. The book is also useful to current practitioners at financial institutions as a ready-reference to common development problems and techniques., Table of Contents, The Fixed-Income MarketThe Equities MarketC++ Programming Techniques in FinanceCommon Libraries for Financial CodeDesigning Numerical ClassesPlotting Financial DataLinear AlgebraInterpolationCalculating Roots of EquationsNumerical IntegrationSolving Partial Differential EquationsAlgorithm OptimizationPortfolio OptimizationMonte Carlo Methods for Equity marketsExtending Financial LibrariesC++ with R and OctaveMultithreadingAppendix A: C++14 Features

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值