关于类,你需要知道的所有

· · 算法·理论

众所周知,Python是一种面向对象的语言,而C不是。

为啥呢?

因为Python引入了Class!

那都说C++也是面向对象的语言,C++有Class吗?

有!跟结构体差不多!

例如,下面这段代码:

struct OIer{
    int age;
    string name;
};

如果改用Class,就是这个样子:

class OIer{
    public:
        int age; // 这里的内容我们叫属性
        string name;
};

咱来解释一下有啥变化。其实就一个:Public(除了这点,Class和Struct没啥区别)。这是因为每一种面向对象语言都有要对其成员进行保护的需求。Class这货默认是Private(你可以理解为要单独写访问函数才能访问,否则 CE,不过写工程的时候为保证数据安全,比较常用),要在前面加上public:才能正常访问。

访问函数咋写?

写成这样就行:

class OIer{
    int age;
    string name;
    public:
        int getAge() {
            return age;
        }
        string getName() {
            return name;
        }
        void haveAge(int a) {
            age = a;
        }
        void haveName(string s) {
            name = s;
        }
}Leo2011; // 在这里,Leo2011就是一个对象

好吧,挺长的。因此我们一般在数据简单(比如做题)的情况下用结构体,复杂的情况下(比如写能赚$的程序的时候)用类。

C++对Private是限制的非常死的,不用访问函数,外面,乃至包括他的子类,都无法访问(这点要夸一下C++,Python中设计了一个“强制访问”,可以越过这项检查,C++相比之下要安全一点),试图访问private里的东西就跟改变一个前面加了const修饰的常量一样,会CE!

那啥是子类啊?

举个栗子!

我们,既属于"OIer",又属于"Student",显然OIer都是学生(不然那叫教练),所以"OIer"就是"Student"的子类。

class Student {
    protected:
        void win() {
            printf("I AK IELTS\n");
        }
}

class OIer public: Student{ // 这里是public继承
    /*
    我们一般管OIer叫派生类或子类,Student叫基类或父类
    */
    int age;
    string name;
    public:
        int getAge() {
            return age;
        }
        string getName() {
            return name;
        }
        void haveAge(int a) {
            age = a;
        }
        void haveName(string s) {
            name = s;
        }
};

TMD 这Protected又是个啥啊

这个比较特殊,只能在子类和基类访问。

具体权限如下表(public继承),请务必要分清:

类型/位置 基类 派生类 外部
Public
Protected ×
Private × ×

protected/private继承会把public和protected在子类中变为对应的级别。

接下来!我要讲个很重要的东西,那就是——类的多态

啥叫多态呢?看这段代码:

class Student {
    protected:
        void win() {
            printf("I AK IELTS\n");
        }
};

class OIer public: Student{
    int age;
    string name;
    public:
        void win() {  // 这个地方也叫重写win函数,重写后的函数名字和参数都是相同的,所以这里既有多态也有重写。不同就没有重写了。当然,父类中用private声明的东西访问都访问不了,重写是不可能滴。
            printf("| /-\|< |o|!!!\n");
        }
        int getAge() {
            return age;
        }
        string getName() {
            return name;
        }
        void haveAge(int a) {
            age = a;
        }
        void haveName(string s) {
            name = s;
        }
};

这里,OIer 类和 Student 类都有一个 win 函数,但它们的内容是不同的。这就是多态。

还有另一个东西:重载

一般是这德行:

bool operator< (const OIer &cmp) const {  // OIer是类的名称,其它照抄就好
    return a < cmp. a;  // sort的比较函数咋写的你就咋写
    /*return this->a < cmp.a*/
}

其中,"this->"是一个指向自己的特殊指针。不过在类当中,默认都是自己,写和不写没啥区别。

请注意,以下运算符不可重载

请注意,不允许通过重载定义新的运算符

这种情况下,重载和多态的区别是,重载的内容来自 C++,多态的内容来自父类。

不过还有一种函数重载,如下:

class printData {
   public:
      void print(int i) {
        cout << "整数为: " << i << endl;
      }

      void print(double  f) {
        cout << "浮点数为: " << f << endl;
      }

      void print(char c[]) {
        cout << "字符串为: " << c << endl;
      }
};

/*假设现在在main函数内*/
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);

这里,对于不同的类型调用了不同的 print 函数,也就是函数重载了。但请注意,如果指示返回值不同,参数列表和名称相同也不构成重载。

最后,讲一下构造函数。

默认情况下,在你定义了对象但是又没有给它的属性赋值的时候,它的值就是全局变量时候的默认值。例如整数就是 0,字符串就是空,等等。

但有的时候,我们想要有一种默认情况,就会用到构造函数。构造函数不负责构造对象,只负责初始化对象

咋写?

写成这样就行:

// 前面定义了 1 个包含 _year, _month 和 _day 的类 Date
Date(int year, int month, int day) {  // 构造函数名=类名,注意构造函数和待会儿要讲的析构函数都不能有返回值定义,即不能写bool Date或void Date作为Date类的构造函数定义
        _year = year;
        _month = month;
        _day = day;
    }

/* main函数内 */
Date d(2020, 4, 27);

需要注意的是,写了构造函数后不给构造函数传足够的参数会 CE, 比如你不能写 Date d;Date d(3, 3);,但是你也可以重载,来应付多种情况。

类似的还有析构函数,跟构造函数类似,只不过它是在对象被销毁(比如程序结束)的时候自动跑的,跟构造函数差不多,就不细讲了。唯一的区别就是析构函数在定义前要加一个~

下面是个大家伙:类的嵌套!

啥意思?

还是看Code:

class node{
    public:
    class place {
        int x, y;
    }nodePlace;
    int w;
};

/* main函数内 */
node nde;
nde::place = {3, 5}; 
/* 与
  nde::place.x = 3;
  nde::place.y = 5;
 等价 */
nde::place.w = 7;

上面这段代码中,你在node类中新定义了一个place类,那么你就得先定义一个node类的对象(比如nde),然后通过nde::place的方式访问。

upd:好吧我又来更新了,这次讲一讲typedef。

用法也很简单:

typedef class Person {
/*1t代码*/
}mangaer;

typedef Person PM;

这段代码相当于:

class Person {
/*代码*/
};

typedef Person manager;
typedef Person PM;

此时,因为前面有typedef,因此后面本来负责对象定义的地方现在变成了起别名的地方。当然,你在后面继续起也没问题~

就讲到这了,好吧,这篇文章非常长,初赛考到别懵13哈!

参考资料(排名不分先后):

  1. 构造函数详解

  2. 菜鸟教程——C++教程

蒟蒻所写,有问题欢迎私信。

upd:感谢 @GavinCQTD 同志帮忙发现了个错别字的bug,已修复