类(一)

现在介绍一个C++中最重要的概念:类。

C++内存在了大量的内置类型,比如int表示整形(整数),double表示浮点(小数)。在了解类之前,同学们仅仅知道去用各种类型,但并不知道类的机理,也不知道C++可以自己创建类。C++中同学们可以自定义自己的类型,在类的基础上,实现数据的封装(数据隐藏)、多态、继承以及代码复用。这些特性,是自定义类的目的。

现在看下面的代码,其定义了一个非常简单的类型:

#include <iostream>

using namespace std;

class myInt
{
private:

public:
    int test = 0;
};

int main()
{
    myInt myClass;

    cout<< myClass.test;

    return 0;
}

在这个代码段中,同学们通过class关键词自定义了一个类型为myInt。所有类的定义是以关键字 class为开头,后跟类的名称。类的主体包含在一对花括号{}中。类定义后必须跟着一个分号;

随后的private:public:关键词稍后讲解。其主要涉及类的数据隐藏和封装。

在类myInt的花括号内,int test = 0;声明了一个整形数据test。在这里要注意,一些C++内置类型比如int其只能为一个值,但同学们的自定义类型内可以包含若干的数据。其类似C++中的结构struct。在myInt中,还可以包含更多的数据,如:

class myInt
{
private:

public:
    int test = 0;
    double test2 = 3.1;
    int test5 = 4;
};

其表示myInt中包含了三个数据。

main()主函数中则演示了如何调用myInt的数据。首先,myInt为一个类型,因此需要首先创建一个myIntmyInt myClass;(类似int myClass)。其名为myClass。注意myClass为同学们的自定义myInt类型,其中可能包含若干数据而不是一个数据。并且名字可以随便写。在调用myClass内部的数据的时候,需要指定具体的数据名称,如:

myClass.test;

其表示调用名为myClassmyInt类型的内部的test数据。.为成员访问运算符。随后,cout函数对其值进行输出。

上面就是一个简单的类型的范例。C++中的类不仅可以包含数据,还可以包含函数,如:

#include <iostream>

using namespace std;

class myInt
{
private:

public:
    int test = 0;

    void output()
    {
        cout<< test;
    }
};

int main()
{
    myInt myClass;

    myClass.output();

    return 0;
}

相比较于上面的代码块,本段代码在同学们自定义类型myInt中增加了一个函数output(),其输出myInt类型中数据test的值。在主函数中,myClass.output();即调用output()函数进行输出。

现在,我们将最开始的代码进行一下改动:

#include <iostream>

using namespace std;

class myInt
{
private:
    int test = 0;
public:
    //int test = 0;
};

int main()
{
    myInt myClass;

    cout<< myClass.test;

    return 0;
}

上述代码把int test = 0;public:后移入到private:内,在尝试运行的时候,会提示:

main.cpp: In function ‘int main()’:
main.cpp:16:16: error: ‘int myInt::test’ is private
     int test = 0;

数据隐藏

现在可以讨论一下privatepublic关键词。private表示其中的数据为自定义类型的私有数据,即在自定义类型之外不能访问。也就是说,只有class内部的代码才能调用这个私有数据。public表示其中的数据为自定义类型的公有数据,在自定义类型之外也可以访问。在上面这个例子中,main()函数内,myClass.test;表示调用自定义类型内的数据test,但test类型为私有的(private),因此不能通过编译。

如果test类型为私有的,如何访问呢?

可以通过类内定义的公有成员函数来进行访问,这是一种迂回的战术。如:

#include <iostream>

using namespace std;

class myInt
{
private:
    int test = 0;
public:
    void output()
    {
        cout << test;
    }
};

int main()
{
    myInt myClass;

    myClass.output();

    return 0;
}

这段代码将output()定义在public后,表示其为一个公有的成员函数。在自定义类型之外可以进行调用。这是一种迂回的操作,曲线救国,实现了对私有成员函数的访问。下面是一个稍微复杂的例子:

#include <iostream>

using namespace std;

class myInt
{
private:
    //私有数据成员a_
    int a_;
    //私有数据成员b_
    int b_;
    //私有数据成员c_,值为99
    int c_ = 99;

public:
    //公有成员函数,对a_赋值
    void assignA(int a)
    {
        a_ = a;
    }

    //公有成员函数,对b_赋值
    void assignB(int b)
    {
        b_ = b;
    }

    //公有成员函数,对c_赋值 = a_和b_的和
    void sum()
    {
        c_ = a_ + b_;
    }

    //公有成员函数,输出c_的值
    void output()
    {
        cout << c_ << endl;
    }
};

int main()
{
    myInt myClass;

    myClass.assignA(4);

    myClass.assignB(10);

    //在执行sum()之前,c_的值为99,输出99
    myClass.output();

    myClass.sum();

    //在执行sum()之后,c_的值为14,输出14
    myClass.output();

    return 0;
}

接口

类的初始意图就是尽可能的对类内的数据进行隐藏(定义在private内的私有数据)。在对类内数据隐藏之后,类外(比如main函数)是不能调用类内隐藏的数据的。但类的公有成员函数(定义在public内的函数)可以在类外进行调用。这些公有的成员函数也被称之为接口

接口形象的实现类外代码访问类内隐藏的私有数据的一个途径。

封装

上面的类代码看起来比较雍容,最重要的,其还不能实现类的封装。

在某些情况下,一些闭源的代码不想让同学们获取某些计算的细节。比如上个代码中的sum()函数,类的设计者可能并不想让代码泄露出去。这种理念就是类的封装:将公有接口(定义在public内的函数声明)和具体的实现细节(定义在public内的函数的代码)分开。下面的代码将对同学们起到一点启发作用:

#include <iostream>

using namespace std;

//类声明,位于main()函数之前
class myInt
{
private:
    int a_;
    int b_;
    int c_ = 99;

public:
    void assignA(int a);

    void assignB(int b);

    void sum();

    void output();
};

int main()
{
    myInt myClass;

    myClass.assignA(4);

    myClass.assignB(10);

    myClass.output();

    myClass.sum();

    myClass.output();

    return 0;
}

//共有成员函数代码实现
void myInt::assignA(int a)
{
    a_ = a;
}

//共有成员函数代码实现 
void myInt::assignB(int b)
{
    b_ = b;
}

//共有成员函数代码实现    
void myInt::sum()
{
    c_ = a_ + b_;
}

//共有成员函数代码实现 
void myInt::output()
{
    cout << c_ << endl;
}

这一段代码实现的和上一段代码是完全相同的内容。但是类内公有成员函数的实现细节和声明分别定义。具体的,main()函数之前的为类声明,其中公有函数部分也仅仅进行了声明。在main()函数之后,所有公有函数需要定义具体的实现细节。比如,

void myInt::assignA(int a)
{
    a_ = a;
}

中即为普通的函数的定义。但需要注意的是,assignA(int a)之前需要指定其属于类的函数,这通过类名myInt和作用于操作符::类实现。因此void myInt::assignA(int a)即表示返回类型为voidmyInt类型内的assignA()函数,传入参数为int a

上述将类声明和类实现分开定义的写法是实现封装的基本过程。

OpenFOAM实例

下面是摘自OpenFOAM粘度代码的一段实例,其中省略了一些还没有涉及到的内容。

/*---------------------------------------------------------------------------*\
                           Class Newtonian Declaration
\*---------------------------------------------------------------------------*/
//自定义类型,名称为Newtonian
class Newtonian
{
    // 私有成员数据
    // private:可以省略

        //私有dimensionedScalar类型,名称为nu0_
        dimensionedScalar nu0_;

        volScalarField nu_;


public:

    //略去一些尚没有介绍的公有成员函数
    ...


    // 公有成员函数

        //- nu()函数返回volScalarField类型
        volScalarField nu()
        {
            return nu_;
        }

        //- 声明correct()函数
        void correct();
};

现在,同学们应该可以从上段代码中看出:

  • 这是一个类声明,类的名字为Newtonian(从class Newtonian可以判断);

  • 类内的私有数据为nu0_nu,其分别为dimensionedScalarvolScalarField类型;这两个数据只能在类内进行访问(私有数据);

  • 类存在一个公有函数nu(),其直接返回类的私有数据nu_nu()函数为一个接口;

results matching ""

    No results matching ""