在本課中,你將學習如何用 C++ 編寫類。像之前的課程同樣,你須要比較 Python 的編程方式和 C++ 中編程方式的不一樣。python
咱們直接看例子。下面是一個名爲 「Gaussian」 的 Python 類代碼。該類包含兩個類變量:平均值 「mu」,以及方差 「sigma2」。react
你學太高斯分佈,並在以前的納米學位中看過這些方程。ios
類包括三個函數:c++
evaluate
,它表示機率密度函數 。multiply
,它將兩個高斯分佈相乘。add
,它將兩個高斯分佈相加。class Gaussian(): def __init__(self, mean, variance): self.mu = mean self.sigma2= variance def evaluate(self, x): coefficient = 1.0 / sqrt(2.0 * pi *self.sigma2) exponential = exp(-0.5 * (x-self.mu) ** 2 / self.sigma2) return coefficient * exponential def multiply(self, other): # 計算新均值 denominator =self.sigma2+other.sigma2 numerator = self.mu * other.sigma2 + other.mu * self.sigma2 new_mu = numerator / denominator # 計算新方差 new_var =1.0/ ( (1.0/self.sigma2) + (1.0/other.sigma2) ) # 生成新的高斯分佈 return Gaussian(new_mu, new_var) def add(self, other): new_mu = self.mu + other.mu new_sigma2 =self.sigma2+other.sigma2 return Gaussian(new_mu, new_sigma2)
如今,咱們來看看 C++ 中的的一個等價類。和你看到的其餘例子同樣,C++ 代碼更長,而且有 Python 版本沒有的方面。編程
例如,你會注意到,在 C++ 類中,全部的變量及其全部的函數都須要在編寫實現以前先聲明。類還有一部分標記爲private
,另外一部分標記爲public
。此外,C++ 類還包括諸如setMu
、setSigma2
、getMu
和getSigma2
等額外的函數。後端
你將在本課中瞭解全部這些差別。如今,請仔細閱讀代碼,看看你可否理解 set 函數和 get 函數的功能。markdown
# include <math.h> class Gaussian { private: float mu, sigma2; public: // constructor functions Gaussian (); Gaussian (float, float); // change value of average and standard deviation void setMu(float); void setSigma2(float); //輸出平均值和標準差的值 float getMu(); float getSigma2(); //待評估函數 float evaluate (float); Gaussian multiply (Gaussian); Gaussian add (Gaussian); }; Gaussian::Gaussian() { mu = 0; sigma2 = 1; } Gaussian::Gaussian (float average, float sigma) { mu = average; sigma2 = sigma; } void Gaussian::setMu (float average) { mu = average; } void Gaussian::setSigma2 (float sigma) { sigma2 = sigma; } float Gaussian::getMu () { return mu; } float Gaussian::getSigma2() { return sigma2; } float Gaussian::evaluate(float x) { float coefficient; float exponential; coefficient = 1.0 / sqrt (2.0 * M_PI * sigma2); exponential = exp ( pow (-0.5 * (x - mu), 2) / sigma2 ); return coefficient * exponential; } Gaussian Gaussian::multiply(Gaussian other) { float denominator; float numerator; float new_mu; float new_var; denominator = sigma2 + other.getSigma2(); numerator = mu * other.getSigma2() + other.getMu() * sigma2; new_mu = numerator / denominator; new_var = 1.0 / ( (1.0 / sigma2) + (1.0 / other.sigma2) ); return Gaussian(new_mu, new_var); } Gaussian Gaussian::add(Gaussian other) { float new_mu; float new_sigma2; new_mu = mu + other.getMu(); new_sigma2 = sigma2 + other.getSigma2(); return Gaussian(new_mu, new_sigma2); }
在深刻了解如何編寫 C++ 類的細節以前,首先要了解如何在 main.cpp 程序中使用類。在處理大項目時,你可能須要使用一個類,而實際上並不負責實施該類。函數
下面是一個使用 Gaussian 類的 main.cpp 文件。學習
首先,你的 main.cpp 文件須要在文件的頂部聲明全部的變量和函數。而後,main() 函數須要有權訪問該類,並可使用該類的方法:ui
# include <iostream> //聲明類 class Gaussian { private: float mu, sigma2; public: //構造函數 Gaussian (); Gaussian (float, float); //改變均差和標準誤差值 void setMu(float); void setSigma2(float); //輸出均差和標準誤差值 float getMu(); float getSigma2(); //待評估函數 float evaluate (float); Gaussian mul (Gaussian); Gaussian add (Gaussian); }; int main () { Gaussian mygaussian(30.0,20.0); Gaussian othergaussian(10.0,30.0); std::cout << "average " << mygaussian.getMu() << std::endl; std::cout << "evaluation " << mygaussian.evaluate(15.0) << std::endl; std::cout << "mul results sigma " << mygaussian.mul(othergaussian).getSigma2() << std::endl; std::cout << "mul results average " << mygaussian.mul(othergaussian).getMu() << std::endl; std::cout << "add results sigma " << mygaussian.add(othergaussian).getSigma2() << std::endl; std::cout << "add results average " << mygaussian.add(othergaussian).getMu() << std::endl; return 0; }
接下來,咱們學習類的編程步驟。
首先,你須要在程序的頂部聲明該類。而後,在 main 函數內部,你能夠實例化對象:
Gaussian mygaussian(30.0,20.0); Gaussian othergaussian(10.0,30.0);
第一個對象叫作 mygaussian,平均值爲 30,方差爲 20。第二個對象叫作 othergaussian,平均值爲 10,方差爲 30。
而後,你能夠經過如下代碼使用對象方法:
mygaussian.getMu()
它能夠輸出 mygaussian 對象內的平均值。
另外一個例子:
mygaussian.add(othergaussian)
它能夠把 mygaussian 和 othergaussian 相加。
下面是在這個代碼中使用的全部文件,方便你觀察 main.cpp 和 gaussian.cpp 之間的關係
這個程序首先經過如下命令被編譯,但你在後端看不到:
g++ main.cpp gaussian.cpp
#include <math.h> /* sqrt, exp */ // class declaration class Gaussian { private: float mu, sigma2; public: // constructor functions Gaussian (); Gaussian (float, float); // change value of average and standard deviation void setMu(float); void setSigma2(float); // output value of average and standard deviation float getMu(); float getSigma2(); // functions to evaluate float evaluate (float); Gaussian mul (Gaussian); Gaussian add (Gaussian); }; Gaussian::Gaussian() { mu = 0; sigma2 = 1; } Gaussian::Gaussian (float average, float sigma) { mu = average; sigma2 = sigma; } void Gaussian::setMu (float average) { mu = average; } void Gaussian::setSigma2 (float sigma) { sigma2 = sigma; } float Gaussian::getMu () { return mu; } float Gaussian::getSigma2() { return sigma2; } float Gaussian::evaluate(float x) { float coefficient; float exponential; coefficient = 1.0 / sqrt (2.0 * M_PI * sigma2); exponential = exp ( pow (-0.5 * (x - mu), 2) / sigma2 ); return coefficient * exponential; } Gaussian Gaussian::mul(Gaussian other) { float denominator; float numerator; float new_mu; float new_var; denominator = sigma2 + other.getSigma2(); numerator = mu * other.getSigma2() + other.getMu() * sigma2; new_mu = numerator / denominator; new_var = 1.0 / ( (1.0 / sigma2) + (1.0 / other.sigma2) ); return Gaussian(new_mu, new_var); } Gaussian Gaussian::add(Gaussian other) { float new_mu; float new_sigma2; new_mu = mu + other.getMu(); new_sigma2 = sigma2 + other.getSigma2(); return Gaussian(new_mu, new_sigma2); }
開始編寫你本身的 C++ 類以前,咱們再看一下 Gaussian.cpp 文件中的 Gaussian 類代碼。咱們把類按部分分解。
gaussian.cpp 文件有如下幾部分:
首先,咱們看看本課程前一部分的整個 gaussian.cpp 代碼:
# include <math.h> class Gaussian { private: float mu, sigma2; public: //構造函數 Gaussian (); Gaussian (float, float); //改變均差和標準誤差的值 void setMu(float); void setSigma2(float); //輸出均差和標準誤差的值 float getMu(); float getSigma2(); //待評估函數 float evaluate (float); Gaussian multiply (Gaussian); Gaussian add (Gaussian); }; Gaussian::Gaussian() { mu = 0; sigma2=1; } Gaussian::Gaussian (float average, float sigma) { mu = average; sigma2= sigma; } void Gaussian::setMu (float average) { mu = average; } void Gaussian::setSigma2 (float sigma) { sigma2= sigma; } float Gaussian::getMu () { return mu; } float Gaussian::getSigma2() { returnsigma2; } float Gaussian::evaluate(float x) { float coefficient; float exponential; coefficient = 1.0 / sqrt (2.0 * M_PI * sigma2); exponential = exp ( pow (-0.5 * (x - mu), 2) / sigma2 ); return coefficient * exponential; } Gaussian Gaussian::multiply(Gaussian other) { float denominator; float numerator; float new_mu; float new_var; denominator = sigma2 + other.getSigma2(); numerator = mu * other.getSigma2() + other.getMu() * sigma2; new_mu = numerator / denominator; new_var =1.0/ ( (1.0/sigma2) + (1.0/other.sigma2) ); return Gaussian(new_mu, new_var); } Gaussian Gaussian::add(Gaussian other) { float new_mu; float new_sigma2; new_mu = mu + other.getMu(); new_sigma2 = sigma2 + other.getSigma2(); return Gaussian(new_mu, new_sigma2); }
在文件的頂部,你能夠放置任何 include 語句以及類定義。在本例中,include 語句容許 Class 訪問標準庫中的數學文件。
# include <math.h>
類聲明在 include 語句以後。
類聲明與你已經看到的變量和函數聲明相似。你須要聲明全部的類變量名稱、類函數名稱以及它們的類型:
class Gaussian { private: float mu, sigma2; public: //構造函數 Gaussian (); Gaussian (float, float); //改變均差和標準誤差的值 void setMu(float); void setSigma2(float); //輸出均差和標準誤差的值 float getMu(); float getSigma2(); //待評估函數 float evaluate (float); Gaussian multiply (Gaussian); Gaussian add (Gaussian); };
請注意,每一個類的函數和變量都須要聲明。全部類方法均可以使用 mu 和 sign2 這兩個浮點變量;可是,一些類的函數實際上有本身的變量。一個例子是 evaluate 函數。若是你看一下 evaluate 函數的實現,你會看到這個函數有本身的變量:
float coefficient; float exponential;
這兩個變量不是類變量;coefficient 和 exponential 只能用於 evaluate 函數。
你可能已經注意到,其中一些聲明在標爲「private」的部分,其餘聲明則在標爲「public」的部分。理解 private 和 public 之間的區是本課的目標之一。
接下來是構造函數的定義。當你真正使用你的類來實例化一個對象時,這些函數會被調用。Python 有一個功能相同的語法 __init__
。
def __init__(self, variable1, variable2, ..., variablen):
第一個構造函數用於在不指定平均值和方差的狀況下實例化一個對象:
Gaussian::Gaussian() {
mu = 0; sigma2=1; }
另外一個構造函數指定在你肯定平均值和方差時要執行的操做:
Gaussian::Gaussian (float average, float sigma) { mu = average; sigma2= sigma; }
其他代碼包含你的類中全部的函數的定義,也稱爲方法。
get 和 set 函數專門用於獲取變量或更改私有變量的值。咱們會在本課後面部分詳細討論這個問題。
void Gaussian::setMu (float average) { mu = average; } void Gaussian::setSigma2 (float sigma) { sigma2= sigma; } float Gaussian::getMu () { return mu; } float Gaussian::getSigma2() { returnsigma2; }
其他的函數 (evaluate、multiply、add) 和 Python 的類中的函數是同樣的。
float Gaussian::evaluate(float x) { float coefficient; float exponential; coefficient = 1.0 / sqrt (2.0 * M_PI * sigma2); exponential = exp ( pow (-0.5 * (x - mu), 2) / sigma2 ); return coefficient * exponential; } Gaussian Gaussian::multiply(Gaussian other) { float denominator; float numerator; float new_mu; float new_var; denominator = sigma2 + other.getSigma2(); numerator = mu * other.getSigma2() + other.getMu() * sigma2; new_mu = numerator / denominator; new_var =1.0/ ( (1.0/sigma2) + (1.0/other.sigma2) ); return Gaussian(new_mu, new_var); } Gaussian Gaussian::add(Gaussian other) { float new_mu; float new_sigma2; new_mu = mu + other.getMu(); new_sigma2 = sigma2 + other.getSigma2(); return Gaussian(new_mu, new_sigma2); }
在前面的例子中,你看到了如何將一個類分紅一個獨立於 main.cpp 的文件(gaussian.cpp)。可是,主程序文件和 gaussian 類文件都須要在代碼頂部進行徹底相同的類聲明:
//類聲明 class Gaussian { private: float mu, sigma2; public: //構造函數 Gaussian (); Gaussian (float, float); //改變均差和標準誤差的值 void setMu(float); void setSigma2(float); //輸出均差和標準誤差的值 float getMu(); float getSigma2(); //待評估函數 float evaluate (float); Gaussian mul (Gaussian); Gaussian add (Gaussian); };
不須要寫兩次聲明,只須要將聲明寫入頭文件便可。而後,你能夠用一行代碼包含整個聲明:
#include <iostream> #include "gaussian.h" int main () { Gaussian mygaussian(30.0,20.0); Gaussian othergaussian(10.0,30.0); std::cout << "average " << mygaussian.getMu() << std::endl; std::cout << "evaluation " << mygaussian.evaluate(15.0) << std::endl; std::cout << "mul results sigma " << mygaussian.mul(othergaussian).getSigma2() << std::endl; std::cout << "mul results average " << mygaussian.mul(othergaussian).getMu() << std::endl; std::cout << "add results sigma " << mygaussian.add(othergaussian).getSigma2() << std::endl; std::cout << "add results average " << mygaussian.add(othergaussian).getMu() << std::endl; return 0; }
#include <math.h> /* sqrt, exp */ #include "gaussian.h" Gaussian::Gaussian() { mu = 0; sigma2 = 1; } Gaussian::Gaussian (float average, float sigma) { mu = average; sigma2 = sigma; } void Gaussian::setMu (float average) { mu = average; } void Gaussian::setSigma2 (float sigma) { sigma2 = sigma; } float Gaussian::getMu () { return mu; } float Gaussian::getSigma2() { return sigma2; } float Gaussian::evaluate(float x) { float coefficient; float exponential; coefficient = 1.0 / sqrt (2.0 * M_PI * sigma2); exponential = exp ( pow (-0.5 * (x - mu), 2) / sigma2 ); return coefficient * exponential; } Gaussian Gaussian::mul(Gaussian other) { float denominator; float numerator; float new_mu; float new_var; denominator = sigma2 + other.getSigma2(); numerator = mu * other.getSigma2() + other.getMu() * sigma2; new_mu = numerator / denominator; new_var = 1.0 / ( (1.0 / sigma2) + (1.0 / other.sigma2) ); return Gaussian(new_mu, new_var); } Gaussian Gaussian::add(Gaussian other) { float new_mu; float new_sigma2; new_mu = mu + other.getMu(); new_sigma2 = sigma2 + other.getSigma2(); return Gaussian(new_mu, new_sigma2); }
class Gaussian { private: float mu, sigma2; public: // constructor functions Gaussian (); Gaussian (float, float); // change value of average and standard deviation void setMu(float); void setSigma2(float); // output value of average and standard deviation float getMu(); float getSigma2(); // functions to evaluate float evaluate (float); Gaussian mul (Gaussian); Gaussian add (Gaussian); };
在本課接下來的部分,你將實現一個矩陣類,就像你在 Python 面向對象編程課程中所作的同樣。
如今,咱們假定你已經熟悉了基本的矩陣運算。因此本課的重點是練習編寫 C++ 類。
你的第一個任務是在 Matrix 類中聲明變量。下面是聲明 C++ 類的通常語法,供參考:
class Classname { private: declare private variables; declare private functions; public: declare public variables; declare public functions; };
實際聲明變量的代碼與其餘 C++ 變量聲明相同:
datatype variablename;
Matrix 類有三個私有變量:
行和列變量應該聲明爲 size_type。size_type 變量保存向量的大小。size_type 聲明以下所示:
std::vector<int>::size_type variablename;
#include <iostream> #include <vector> #include "matrix.h" int main () { // TODO: Nothing to do here return 0; }
include <vector> using namespace std; // Header file for the Matrix class /* ** TODO: ** Declare the following private variables: ** a 2D float vector variable called grid ** a vector size_type variable called rows ** a vector size_type variable called cols */ class Matrix { private: vector< vector<float> >grid; private: vector<int> rows; private: vector<int>cols; };
要在 Matrix 類中編寫函數,首先須要聲明這些函數。對於 Matrix 類,你能夠將這些函數看做屬於三個獨立的類別:
聲明這些函數的方法和前一課中聲明函數同樣。不一樣的是,如今你必須決定某個函數是私有的、受保護的仍是公共的。函數聲明在類聲明內部。
你須要在 matrix.cpp 中定義你的函數。但首先,讓咱們簡要地談一下每種類型的函數。構造函數用於初始化對象。Python 使用def __init__
語法實現這一功能。C++ 語法有點不一樣,你將在本課的下一部分中瞭解這些差別。
set 和 get 取函數專門用於訪問和賦值給私有變量。由於一個對象不能直接訪問私有變量,set 和 get 函數提供了間接訪問功能。set 和 get 函數的語法和其餘 C++ 函數相同。使用 set 和 get 是面向對象編程的慣例,而不是特定的 C++ 語法。
最後,還有一些由矩陣功能組成的函數,如打印矩陣、矩陣相加、矩陣乘法等等。在練習中,你會執行這些函數。
下面,咱們進入到下一部分的練習,學習如何聲明和定義 Matrix 構造函數。
Set 和 Get 函數容許你的對象訪問私有變量。對象不能直接訪問私有變量,因此須要使用 set 和 get 函數。本課前面的 Gaussian 對象展現了具體實現方法。
如下是 set 和 get 函數的聲明:
class Gaussian { private: ... public: ... void setMu(float); void setSigma2(float); float getMu(); float getSigma2(); .... };
set 函數改變一個變量的值,而 get 函數則返回一個變量的值。你會注意到,set 和 get 函數的語法與全部常規函數都是同樣的。實際上,set 和 get 是約定,而不是 C++ 特有的。傳統上,咱們將這些函數命名爲 getVariablename() 和 setVariablename(),雖然沒有明確要求。
你須要將 set 和 get 函數聲明爲公共的,以便對象能夠訪問這些函數。
第三組要聲明的函數是用於矩陣功能的。其語法與 get 和 set 函數語法以及任何正常的 C++ 函數徹底相同。你須要爲輸入變量指定返回數據類型、函數名稱和數據類型。
例如,Gaussian 類有三個函數:evaluate、multiply 和 add。如下是如何在 gaussian.h 文件中聲明這些函數的示範:
class Gaussian { .... public: ... //待評估函數 float evaluate (float); Gaussian multiply (Gaussian); Gaussian add (Gaussian); };
Python 和 C++ 都有構造函數。構造函數定義了對象實例化時會發生什麼。
它們定義了對象實例化時會發生什麼。在 Python 中,語法是:
def __init__(self, variable1, variable2, ..., variablen): self.variable1 = variable1 self.variable2 = variable2 self.variablen = variablen
在 C++ 中,構造函數聲明以下:
Classname (datatype for variable1, datatype for variable2, …, datatype for variablen);
你也能夠同時聲明一個默認的構造函數。
Classname ();
實例化一個對象而不提供變量的值時,使用這個默認的構造函數。說得更具體一點,你將用一個二維向量來初始化一個矩陣變量。若是不提供二維向量,也可使用默認向量來初始化矩陣變量。第二種狀況中,可使用空構造函數。
Gaussian 構造函數聲明以下:
class Gaussian { private: ... public: ... Gaussian (); Gaussian (float, float); .... };
聲明瞭構造函數後,你須要在 .cpp 文件中實際定義它們。
構造函數定義的語法以下:
//空構造函數的語法 Classname::ClassName() { constructor function definition } // constructor function syntax Classname::ClassName(datatype variable1, datatype variable2, …, datatype variablen) { constructor function definition }
你能夠看到這在 Gaussian 類中是如何實現的:
Gaussian::Gaussian() {
mu = 0; sigma2 = 1; } Gaussian::Gaussian (float average, float sigma) { mu = average; sigma2 = sigma; }
請注意,構造函數不返回任何內容。它們只會初始化類變量。你可能還想知道,若是 mu 和 sigma2 是私有變量,函數定義如何能訪問這兩個變量。請記住,私有變量能夠從類代碼內部訪問,但不能從類外部訪問。
在 Python 和 C++ 中,均可以在構造函數中使用默認值。在 Python 中,語法是:
def __init__(self, variable1 = default1, variable2 = default2, ..., variablen = defaultn): self.variable1 = variable1 self.variable2 = variable2 self.variablen = variablen
C++ 也有相同功能,但語法可能和所指望的不同。默認值的定義實際上在 .h 文件函數定義中。下面一個加法類的簡單例子,它包含兩個整數並輸出它們的總和。
如下爲頭文件 add.h:
class Add { public: int a; int b; Add(int, int second = 17); int addition(); };
這裏是 add.cpp 中的定義:
# include "add.h" Add::Add(int first, int second) { a = first; b = second; } int Add::addition() { return a + b; }
請注意,默認值是在頭文件中聲明的。如今,若是在實例化 add 對象時只指定了一個值,則變量 b 將具備默認值 17:
# include <iostream> # include "add.h" int main() { Add adder(5); std::cout << adder.addition() << std::endl; return 0; }
上述代碼輸出值 22。
對象不能直接訪問私有變量,但藉助於 Set 和 Get 函數,你的對象能夠訪問私有變量。本課前面的 Gaussian 對象展現了具體實現方法。
如下是 set 和 get 函數的聲明:
class Gaussian { private: ... public: ... void setMu(float); void setSigma2(float); float getMu(); float getSigma2(); .... };
這裏是函數定義:
void Gaussian::setMu (float average) { mu = average; } void Gaussian::setSigma2 (float sigma) { sigma2 = sigma; } float Gaussian::getMu () { return mu; } float Gaussian::getSigma2() { return sigma2; }
定義 set 或 get 函數的語法與其餘類函數(除構造函數外)相同:
return datatype Classname::functionname() { code to define the function; }
實際上,get 和 set 函數是一種約定,而不是具備特殊語法的特殊函數。傳統上,雖然沒有明確要求,但咱們將這些函數命名爲 getVariablename() 和 setVariablename()。
你須要將 set 和 get 函數聲明爲公共的,這樣對象就能夠訪問這些函數了。
繼續編寫你的矩陣類代碼。 使用 set 函數來修改 grid 變量。 全部三個私有變量(gird、rows、cols)都應該有函數。
#include <iostream> #include <vector> #include "matrix.h" int main () { // TODO: Nothing to do here return 0; }
# include "matrix.h" Matrix::Matrix() { std::vector <std:: vector <float> > initial_grid (10, std::vector <float>(5, 0.5)); grid = initial_grid; rows = initial_grid.size(); cols = initial_grid[0].size(); } Matrix::Matrix(std::vector <std:: vector <float> > initial_grid) { grid = initial_grid; rows = initial_grid.size(); cols = initial_grid[0].size(); } void Matrix::setGrid(std::vector< std::vector<float> > new_grid) { grid = new_grid; rows = new_grid.size(); cols = new_grid[0].size(); } std::vector< std::vector<float> > Matrix::getGrid() { return grid; } std::vector<int>::size_type Matrix::getRows() { return rows; } std::vector<int>::size_type Matrix::getCols() { return cols; }
# include <vector> class Matrix { private: std::vector< std::vector<float> > grid; std::vector<int>::size_type rows; std::vector<int>::size_type cols; public: //構造函數 Matrix (); Matrix (std::vector< std::vector<float> >); // set functions void setGrid(std::vector< std::vector<float> >); // get functions std::vector< std::vector<float> > getGrid(); std::vector<int>::size_type getRows(); std::vector<int>::size_type getCols();
Matrix 類的最後一部分涉及到矩陣函數的實現。你須要儘量多地練習矩陣運算編程,包括加法、乘法、轉置、求逆等。
咱們建議,你至少須要實現一個矩陣加法,以及一個名爲 matrix_print 的函數,它使用 cout 將矩陣輸出到終端。在本頁最後給出的參考答案中,咱們還提供了matrix_transpose 函數的代碼。
實現這些類的函數與實現本課前面的 get 和 set 函數是同樣的。你將須要在 matrix.h 中聲明函數,並在 matrix.cpp 中定義函數。通常語法也是同樣的:
return datatype functionname(datatype for variable1, datatype for variable2, ..., datatype for variablen)
return datatype Classname::functionname(datatype variable1, datatype variable2, ..., datatype variablen) { code defining the function; }
在本練習中,你將聲明並定義將兩個矩陣相加的矩陣類函數。如下是矩陣加法函數的輸入和輸出:
輸入:
輸出:
因爲 matrix_addition 函數的輸入是矩陣,所以須要使用 Matrix 類做爲數據類型來聲明並定義函數。這彷佛有點混亂,但和本課前面介紹的 Gaussian 類中的 mul 和 add 函數徹底相同。你可使用這些做爲編寫 matrix_addition 函數的指南。
如下是 gaussian.h 中的 mul 和 add 函數的函數聲明,供參考:
Gaussian mul (Gaussian); Gaussian add (Gaussian);
這兩個函數都接收高斯值並輸出高斯值。如下是 gaussian.cpp 的函數定義:
Gaussian Gaussian::mul(Gaussian other) {
float denominator; float numerator; float new_mu; float new_var; denominator = sigma2 + other.getSigma2(); numerator = mu * other.getSigma2() + other.getMu() * sigma2; new_mu = numerator / denominator; new_var = 1.0 / ( (1.0 / sigma2) + (1.0 / other.sigma2) ); return Gaussian(new_mu, new_var); } Gaussian Gaussian::add(Gaussian other) { float new_mu; float new_sigma2; new_mu = mu + other.getMu(); new_sigma2 = sigma2 + other.getSigma2(); return Gaussian(new_mu, new_sigma2); }
雖然 matrix_addition 函數的實現有所不一樣,但通常結構與 Gaussian 示例中的 mul 和 add 函數相同。
你還須要編寫一個 matrix_print 函數,該函數使用 cout 向終端輸出一個矩陣。matrix_print 函數沒有輸入,也沒有輸出。
在 matrix.cpp 和 matrix.h 代碼中填充 TODO 部分。
# include <vector> # include <iostream> # include <stdexcept> # include <vector> class Matrix { private: std::vector< std::vector<float> > grid; std::vector<int>::size_type rows; std::vector<int>::size_type cols; public: // 構造函數 Matrix (); Matrix (std::vector< std::vector<float> >); // set 函數 void setGrid(std::vector< std::vector<float> >); // get 函數 std::vector< std::vector<float> > getGrid(); std::vector<int>::size_type getRows(); std::vector<int>::size_type getCols(); // 矩陣函數 Matrix matrix_transpose(); Matrix matrix_addition(Matrix); //矩陣打印 void matrix_print(); };
# include "matrix.h" Matrix::Matrix() { std::vector <std:: vector <float> > initial_grid (10, std::vector <float>(5, 0.5)); grid = initial_grid; rows = initial_grid.size(); cols = initial_grid[0].size(); } Matrix::Matrix(std::vector <std:: vector <float> > initial_grid) { grid = initial_grid; rows = initial_grid.size(); cols = initial_grid[0].size(); } void Matrix::setGrid(std::vector< std::vector<float> > new_grid) { grid = new_grid; rows = new_grid.size(); cols = new_grid[0].size(); } std::vector< std::vector<float> > Matrix::getGrid() { return grid; } std::vector<int>::size_type Matrix::getRows() { return rows; } std::vector<int>::size_type Matrix::getCols() { return cols; } Matrix Matrix::matrix_transpose() { std::vector< std::vector<float> > new_grid; std::vector<float> row; for (int i = 0; i < cols; i++) { row.clear(); for (int j = 0; j < rows; j++) { row.push_back(grid[j][i]); } new_grid.push_back(row); } return Matrix(new_grid); } Matrix Matrix::matrix_addition(Matrix other) { if ((rows != other.getRows()) || (cols != other.getCols())) { throw std::invalid_argument( "matrices are not the same size" ); } std::vector< std::vector<float> > othergrid = other.getGrid(); std::vector< std::vector<float> > result; std::vector<float> new_row; for (int i = 0; i < rows; i++) { new_row.clear(); for (int j = 0; j < cols; j++) { new_row.push_back(grid[i][j] + othergrid[i][j]); } result.push_back(new_row); } return Matrix(result); } void Matrix::matrix_print() { std::cout << std::endl; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { std::cout << grid[i][j] << " "; } std::cout << std::endl; } std::cout << std::endl; }
如今,是時候在程序中使用你的矩陣類了!C++ 中實例化對象的語法以下所示:
Classname objectname(inputs for initializing an object of Classname);
而後,你就能夠訪問任何公共變量,如:
objectname.variablename
你還能夠訪問公共函數:
objectname.methodname(inputs)
請記住,你的程序沒法訪問任何私有變量或函數。這就是你須要爲你的私有變量編寫公共的 get 和 set 函數的緣由。
在開始使用矩陣類以前,先看看 Gaussian.cpp 中 main.cpp 的一個例子:
# include <iostream> # include "gaussian.h" int main () { Gaussian mygaussian(30.0,20.0); Gaussian othergaussian(10.0,30.0); std::cout << "average " << mygaussian.getMu() << std::endl; std::cout << "evaluation " << mygaussian.evaluate(15.0) << std::endl; std::cout << "mul results sigma " << mygaussian.mul(othergaussian).getSigma2() << std::endl; std::cout << "mul results average " << mygaussian.mul(othergaussian).getMu() << std::endl; std::cout << "add results sigma " << mygaussian.add(othergaussian).getSigma2() << std::endl; std::cout << "add results average " << mygaussian.add(othergaussian).getMu() << std::endl; return 0; }
如今輪到你編寫矩陣對象了。下面提供了一些輔助代碼,有一些 TODO 部分須要你完成。
//main.cpp 參考答案 # include <iostream> # include <vector> # include "matrix.h" int main () { // 給變量 initial_grid 分配一個 7x5 矩陣 // 矩陣中的全部值都是 0.4 std::vector <std:: vector <float> > initial_grid (7, std::vector <float>(5, 0.4)); // TODO:使用初始 grid 變量來實例化一個矩陣對象 // 矩陣對象應該寫做 matrixa Matrix matrixa(initial_grid); // TODO:使用 matrix_print() 方法打印出 matrixa matrixa.matrix_print(); // TODO:打印出 matrixa 中的行數。你須要 //使用 getRows() 函數和 std::cout std::cout << matrixa.getRows(); // TODO:打印出 matrixa 中的列數 std::cout << matrixa.getCols(); // TODO:取矩陣的轉置並把結果存儲在 //一個名叫 transposea 的變量裏 Matrix transposea = matrixa.matrix_transpose(); // TODO:打印出 transposea transposea.matrix_print(); // 如今你須要使用另外一個名爲 matrixb 的 7x5 矩陣,來 //給出 matrix_addition 函數的結果 // 7x5 二維矩陣,全部值均爲 0.2 std::vector <std:: vector <float> > second_grid (7, std::vector <float>(5, 0.2)); // TODO:實例化一個叫作 matrixb 的對象使用 second_grid // 變量做爲初始化 matrixb 的輸入 Matrix matrixb(second_grid); // TOOD:matrixa 和 matrixb 相加。將結果存儲在一個新的矩陣中 //變量名爲 matrixsum Matrix matrixsum(matrixa.matrix_addition(matrixb)); // TODO:打印出 matrixsum 變量中包含的矩陣 matrixsum.matrix_print(); return 0; }
若是你的計算機上沒有在本地運行 C++,如今是完成這個任務的好機會!
下面是在本地計算機上編譯並運行矩陣代碼的簡要說明。把你的 main.cpp、matrix.cpp 和 matrix.h 文件放到同一個目錄下。在 Linux 和 Mac 上,你可使用以下命令編譯代碼:
g++ main.cpp matrix.cpp
或者你的系統中任何相同功能的程序或編譯器。要編譯代碼,你須要編譯 main.cpp 和 matrix.cpp。而後,你能夠執行你的代碼,參考命令以下:
./a.out