• uda 4.C++面向对象编程


    Python vs C++ 对比课

     

    在本课中,你将学习如何用 C++ 编写类。像以前的课程一样,你需要比较 Python 的编程方式和 C++ 中编程方式的不同。

    我们直接看例子。下面是一个名为 “Gaussian” 的 Python 类代码。该类包含两个类变量:平均值 “mu”,以及方差 “sigma2”。

    你学过高斯分布,并在之前的纳米学位中看过这些方程。

    类包括三个函数:

    1. evaluate,它表示概率密度函数 。
    2. multiply,它将两个高斯分布相乘。
    3. 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) ** 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++ 中的的一个等价类。和你看到的其他例子一样,C++ 代码更长,并且有 Python 版本没有的方面。

    例如,你会注意到,在 C++ 类中,所有的变量及其所有的函数都需要在编写实现之前先声明。类还有一部分标记为private,另一部分标记为public。此外,C++ 类还包括诸如setMusetSigma2getMugetSigma2等额外的函数。

    你将在本课中了解所有这些差异。现在,请仔细阅读代码,看看你能否理解 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 = 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() 函数需要有权访问该类,并可以使用该类的方法:

    # 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 类有三个私有变量:

    • 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 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 类,你可以将这些函数看作属于三个独立的类别:

    • 构造函数
    • 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 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;
    }
    
     

    编写矩阵函数

    在本练习中,你将声明并定义将两个矩阵相加的矩阵类函数。以下是矩阵加法函数的输入和输出:

    输入:

    • 一个矩阵,它将被添加到 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 (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 例子

    在开始使用矩阵类之前,先看看 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
     
  • 相关阅读:
    LeetCode 905 按奇偶排序数组
    LeetCode 46 全排列
    Django 2随便使用笔记-Day01
    Python函数化编程整理
    Oracle解锁表笔记
    springboot(1)使用SpringBoot基础HTTP接口GET|POST|DELETE|PUT请求
    什么是Restful API
    C# 生成条形码BarCode 128
    ADB shell 的一般操作
    遇到“未能从程序集XXXX...加载类型XXX”的问题
  • 原文地址:https://www.cnblogs.com/fuhang/p/9077789.html
Copyright © 2020-2023  润新知