类的概念


类是面向对象编程的核心。我喜欢把类理解为一组数据和一个接口。例如机器人可以是一个类,他有一些数据,如颜色、大小、型号等,还有一个接口,如移动、转动、开关等。
类可以包含成员变量、成员函数、构造函数、析构函数等。成员变量是类的状态,成员函数是类的行为。

类的声明和定义


通常我们将类的定义放在头文件,而类行为的实现放在源文件中。

class Robot{
    // private:  声明成员变量
    // 禁止直接访问,只能通过接口函数访问
    // 在初始化时,通过构造函数初始化
    private:
        string color;
        float size;
        float position[3];
        float rotation[3];
    // public:  声明接口函数
    // 允许直接访问,可以被其他函数调用
    public:
        /*
         构造函数
         构造函数的作用是初始化类的状态
         初始化方法:
            直接初始化,如:Robot r("red", 1.0);
            通过参数初始化,如:Robot r = Robot("red", 1.0);
        */
        Robot(string c, float s, float p[3], float r[3]);
        /*
         析构函数
         析构函数的作用是释放类的资源(定义类的程序块结束后)
         一般不需要内容,除非使用new申请了内存需要手动释放
         */
        ~Robot( );
        void move(float x, float y, float z);
        void rotate(float x, float y, float z);
        void show( );
};

下面是类的实现:
Robot::Robot(string c, float s, float p[3], float r[3]){
    color = c;
    size = s;
    for(int i = 0; i < 3; i++){ 
        position[i] = p[i];
        rotation[i] = r[i];
    }
}

Robot::~Robot( ){
    // 释放内存
}

void Robot::move(float x, float y, float z){
    for(int i = 0; i < 3; i++){
        position[i] += x;
        position[i] += y;
        position[i] += z;
    }
}

void Robot::rotate(float x, float y, float z){
    for(int i = 0; i < 3; i++){
        rotation[i] += x;
        rotation[i] += y;
        rotation[i] += z;
    }
}

void Robot::show( ){
    cout << "color: " << color << endl;
    cout << "size: " << size << endl;
    cout << "position: " << position[0] << " " << position[1] << " " << position[2] << endl;
    cout << "rotation: " << rotation[0] << " " << rotation[1] << " " << rotation[2] << endl;
    }

以上是类的声明和定义,我们可以看到,类中包含了一些成员变量,以及一些接口函数。

怎么理解接口?
接口是服务端为客户端封装好的,客户端只需要调用接口函数,就能完成某些功能。
服务端的责任是实现一些函数,并详细告知如何调用这些函数,以达到客户自己的目的。
而客户端的任务就是详细阅读接口文档,并调用接口函数,完成自己的需求。
所以,接口是服务端和客户端之间沟通的桥梁。

如何使用类


float potions[3] = {1.0, 2.0, 3.0};
float rotation[3] = {0.1, 0.2, 0.3};

// 类的创建和结构类似
Robot robot("red", 1.0, potions, rotation);
// 下面的创造方法实际上生成了一个临时对象,并将其赋值给了robot2
Robot robot2 = Robot("blue", 0.5, potions, rotation); 
// 我们也可以定义一个指针变量指向类对象,但最后需要手动释放内存,因为析构函数没有delete
Robot *p = new Robot("green", 0.8, potions, rotation);
// 如果没有定义构造函数,可以不进行初始化赋值
Robot empty;
// 但如果已经定义了构造函数,这样的初始化是非法的。
// 但我们可以通过函数重载来使其合法
// 在类声明中,我们可以定义一个空参数的构造函数Robot();

// 在c++11中,我们可以用列表初始化的方式来初始化类对象
// 这需要列表内参数和某个构造函数参数一致
Robot robot3 = {"yellow", 0.7, potions, rotation};
Robot robot4{"black", 0.9, potions, rotation};
// 我们可以通过.来访问类的成员函数
robot.move(1.0, 2.0, 3.0);
robot.rotate(0.1, 0.2, 0.3);
robot.show( );

// 但当我们使用const来修饰类对象时,我们不能调用类的成员函数
// 因为成员函数可能修改类的状态,而const对象不能修改状态
// 所以我们可以将某些成员函数声明为const,这样就可以在const对象上调用
const Robot robot5 = robot;
void show() const; // 将shoow最后加上const,表示该函数不修改类的状态,即可被调用

this指针


this指针是一个隐含的指针,指向当前对象的地址。在类的成员函数中,我们可以通过this指针来访问类的成员变量。
假如我们想让一个类的成员函数返回这个类本身,我们可以用this指针来实现。

// 第一个Robot表明这是函数的返回值类型
// 第二个Robot表明这是Robot类的一个成员函数
const Robot& Robot::returnthisrobot( ){
    return *this;
}

作用域


为什么定义类的成员函数是要加上类名称Robot::呢?
原因是我们声明成员函数时,是在类内进行的,所以作用域局限在类内。
因此在类外进行定义时,需要加上类名称。
这也就意味着我们可以在类外定义另一个同名函数,而不会与类的成员函数冲突。

void move(float x, float y, float z);

那为什么在类的成员函数中,我们可以直接使用类内的成员变量呢?
原因是成员函数都是内联函数,因此编译器会将函数体内的变量直接替换成内存地址,因此可以直接访问。

至于类的声明周期,前文提过,当创造类的程序块结束时,类的析构函数会自动调用,释放类占用的资源。

如何在类内定义常量?
```cpp
private:
    const int max_size =  100;
这样的方法是不行的,因为类声明时并没有存储空间,因此无法存储常量。
正确的方法是:
private:
    static const int max_size =  100;
这样的声明方式会在整个程序中创建一个常量,因此可以被所有对象共享。