uda 4.C++面向对象编程
Python vs C++ 对比课
在本课中,你将学习如何用 C++ 编写类。像以前的课程一样,你需要比较 Python 的编程方式和 C++ 中编程方式的不同。
我们直接看例子。下面是一个名为 “Gaussian” 的 Python 类代码。该类包含两个类变量:平均值 “mu”,以及方差 “sigma2”。
你学过高斯分布,并在之前的纳米学位中看过这些方程。
类包括三个函数:
evaluate
,它表示概率密度函数 。multiply
,它将两个高斯分布相乘。add
,它将两个高斯分布相加。
Gaussian 类的 Python 代码
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) ** / 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++ 中的的一个等价类。和你看到的其他例子一样,C++ 代码更长,并且有 Python 版本没有的方面。
例如,你会注意到,在 C++ 类中,所有的变量及其所有的函数都需要在编写实现之前先声明。类还有一部分标记为private
,另一部分标记为public
。此外,C++ 类还包括诸如setMu
、setSigma2
、getMu
和getSigma2
等额外的函数。
你将在本课中了解所有这些差异。现在,请仔细阅读代码,看看你能否理解 set 函数和 get 函数的功能。
# 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 = ;
sigma2 = ;
} 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), ) / 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() 函数需要有权访问该类,并可以使用该类的方法:
# 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 ;
}
接下来,我们学习类的编程步骤。
对象实例化
首先,你需要在程序的顶部声明该类。然后,在 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 = ;
sigma2 = ;
} 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), ) / 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 = ;
sigma2=;
} 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), ) / 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), ) / 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 ;
}
#include <math.h> /* sqrt, exp */
#include "gaussian.h" Gaussian::Gaussian() {
mu = ;
sigma2 = ;
} 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), ) / 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 类有三个私有变量:
- grid - 保存矩阵值de 2D浮点向量
- rows - 矩阵的行数
- columns - 矩阵的列数
行和列变量应该声明为 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 ;
}
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 类,你可以将这些函数看作属于三个独立的类别:
- 构造函数
- set 和 get 函数
- Matrix 功能函数
声明这些函数的方法和前一课中声明函数一样。不同的是,现在你必须决定某个函数是私有的、受保护的还是公共的。函数声明在类声明内部。
你需要在 matrix.cpp 中定义你的函数。但首先,让我们简要地谈一下每种类型的函数。构造函数用于初始化对象。Python 使用def __init__
语法实现这一功能。C++ 语法有点不同,你将在本课的下一部分中了解这些差异。
set 和 get 取函数专门用于访问和赋值给私有变量。因为一个对象不能直接访问私有变量,set 和 get 函数提供了间接访问功能。set 和 get 函数的语法和其他 C++ 函数相同。使用 set 和 get 是面向对象编程的惯例,而不是特定的 C++ 语法。
最后,还有一些由矩阵功能组成的函数,如打印矩阵、矩阵相加、矩阵乘法等等。在练习中,你会执行这些函数。
下面,我们进入到下一部分的练习,学习如何声明和定义 Matrix 构造函数。
Set 和 Get 函数声明
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 构造函数
它们定义了对象实例化时会发生什么。在 Python 中,语法是:
def __init__(self, variable1, variable2, ..., variablen):
self.variable1 = variable1
self.variable2 = variable2
self.variablen = variablen
C++ 构造函数声明
在 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 函数声明
对象不能直接访问私有变量,但借助于 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 和 get 函数
继续编写你的矩阵类代码。 使用 set 函数来修改 grid 变量。 所有三个私有变量(gird、rows、cols)都应该有函数。
#include <iostream>
#include <vector>
#include "matrix.h" int main () { // TODO: Nothing to do here return ;
}
# 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;
}
编写矩阵函数
在本练习中,你将声明并定义将两个矩阵相加的矩阵类函数。以下是矩阵加法函数的输入和输出:
输入:
- 一个矩阵,它将被添加到 grid 变量中
输出:
- 包含 grid 变量矩阵和输入矩阵之和的一个矩阵
由于 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 (, std::vector <float>(, 0.5));
grid = initial_grid;
rows = initial_grid.size();
cols = initial_grid[].size(); } Matrix::Matrix(std::vector <std:: vector <float> > initial_grid) {
grid = initial_grid;
rows = initial_grid.size();
cols = initial_grid[].size();
} void Matrix::setGrid(std::vector< std::vector<float> > new_grid) {
grid = new_grid;
rows = new_grid.size();
cols = new_grid[].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 = ; i < cols; i++) {
row.clear(); for (int j = ; 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 = ; i < rows; i++) {
new_row.clear();
for (int j = ; 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 = ; i < rows; i++)
{
for (int j = ; 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 例子
在开始使用矩阵类之前,先看看 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 ;
}
现在轮到你编写矩阵对象了。下面提供了一些辅助代码,有一些 TODO 部分需要你完成。
//main.cpp 参考答案
# include <iostream>
# include <vector>
# include "matrix.h" int main () { // 给变量 initial_grid 分配一个 7x5 矩阵
// 矩阵中的所有值都是 0.4
std::vector <std:: vector <float> >
initial_grid (, std::vector <float>(, 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 (, std::vector <float>(, 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 ;
}
编译你的程序
如果你的计算机上没有在本地运行 C++,现在是完成这个任务的好机会!
下面是在本地计算机上编译并运行矩阵代码的简要说明。把你的 main.cpp、matrix.cpp 和 matrix.h 文件放到同一个目录下。在 Linux 和 Mac 上,你可以使用如下命令编译代码:
g++ main.cpp matrix.cpp
或者你的系统中任何相同功能的程序或编译器。要编译代码,你需要编译 main.cpp 和 matrix.cpp。然后,你可以执行你的代码,参考命令如下:
./a.out
uda 4.C++面向对象编程的更多相关文章
- python学习笔记15(面向对象编程)
虽然Python是解释性语言,但是它是面向对象的,能够进行对象编程. 一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界 ...
- angular2系列教程(六)两种pipe:函数式编程与面向对象编程
今天,我们要讲的是angualr2的pipe这个知识点. 例子
- 带你一分钟理解闭包--js面向对象编程
上一篇<简单粗暴地理解js原型链--js面向对象编程>没想到能攒到这么多赞,实属意外.分享是个好事情,尤其是分享自己的学习感悟.所以网上关于原型链.闭包.作用域等文章多如牛毛,很多文章写得 ...
- PHP 面向对象编程和设计模式 (1/5) - 抽象类、对象接口、instanceof 和契约式编程
PHP高级程序设计 学习笔记 2014.06.09 什么是面向对象编程 面向对象编程(Object Oriented Programming,OOP)是一种计算机编程架构.OOP 的一条基本原则是计算 ...
- Delphi_09_Delphi_Object_Pascal_面向对象编程
今天这里讨论一下Delphi中的面向对象编程,这里不做过多过细的讨论,主要做提纲挈领的描述,帮助自己抓做重点. 本随笔分为两部分: 一.面向对象编程 二.面向对象编程详细描述 ------------ ...
- python基础-面向对象编程
一.三大编程范式 编程范式即编程的方法论,标识一种编程风格 三大编程范式: 1.面向过程编程 2.函数式编程 3.面向对象编程 二.编程进化论 1.编程最开始就是无组织无结构,从简单控制流中按步写指令 ...
- 面向对象编程(OOP)
什么是面向对象编程,对于面向对象编程与面向过程编程的解释随处可见,个人认为对面向对象编程解释最好的一个定义是:依赖倒转原则是面向对象编程的标志,面向对象编程是一种思想,无论使用哪一种编程语言,如果在编 ...
- python 学习笔记7 面向对象编程
一.概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强..." ...
- 进击的Python【第七章】:Python的高级应用(四)面向对象编程进阶
Python的高级应用(三)面向对象编程进阶 本章学习要点: 面向对象高级语法部分 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 一.面向对象高级语法部分 静态方法 ...
随机推荐
- 关于 linux 的 limit 的设置
以下内容参考链接 1.file-max系统最大打开文件描述符数 永久性:在/etc/sysctl.conf中设置 2.nr_open是单个进程可分配的最大文件数. 内核支持的最大file handle ...
- java-异常处理1
概要图 异常讲解流程图 一 java 异常和错误层次图 1.1 图1 1.2 图2 二 异常生的过程 1 异常可以结束函数. 同时也让程序结束了. 三 异常和错误的发生和区别 Java运行时期发生的问 ...
- [Vue CLI 3] Uglify 相关的应用和设计
在本文开始之前,先留一个问题? 如果在新版本我想加一个 drop_console 的配置呢? 在老版本的脚手架生成的配置中,对于线上环境的文件:webpack.prod.conf.js 使用了插件:u ...
- KiCad 工程用 Git 管理需要忽略哪些文件?
KiCAD 工程用 Git 管理需要忽略哪些文件? KiCAD 使用的 文本格式,天生可以用 Git 来管理. 但是并非所有文件需要使用 Git 管理,以下文件可以忽略. *.bak fp-info- ...
- small标签
<small> 标签将旁注 (side comments) 呈现为小型文本. 免责声明.注意事项.法律限制或版权声明的特征通常都是小型文本.小型文本有时也用于新闻来源.许可要求. 对于由 ...
- 两种方法使vue实现jQuery调用
引言 如果说vue是前端工程化使用较多的骨架,那么JavaScript就是我们的前端的细胞.MVVM模式让我们体验到前端开发的便携,无需再过多的考虑DOM的操作.而vue的渐进式开发(逐步引用组件,按 ...
- 搭建直播服务器,使用nginx与nginx-rtmp-module搭建流媒体服务器;
现在,一起学习一下如何自己搭建一个流媒体服务器吧! 本次搭建流媒体使用的环境是centos 7.0+nginx: 让我们一起开始奇妙的流媒体之旅吧! 1.下载nginx-rtmp-module: ng ...
- Directx11教程(57) 环境映射
原文:Directx11教程(57) 环境映射 建好skydome后,如果我们想让其中的某个物体,比如那个球体来映射出周围环境的蓝天白云(不包括自己附近的物体),该怎么做呢?此时可以把这个 ...
- Directx11教程(42) 纹理映射(12)-简单的bump mapping
原文:Directx11教程(42) 纹理映射(12)-简单的bump mapping 有时候,我们只有一个粗糙的模型,但是我们想渲染纹理细节,比如一个砖墙,我们如何在只有一个平面的时候 ...
- Directx11教程37 纹理映射(7)
原文:Directx11教程37 纹理映射(7) 本章是在教程35.36的基础上来实现一个光照纹理结合的程序,就是把场景中旋转的cube加上纹理. lighttex.vs中顶点的结构现在 ...