关于类,你需要知道的所有
众所周知,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->"是一个指向自己的特殊指针。不过在类当中,默认都是自己,写和不写没啥区别。
请注意,以下运算符不可重载:
.:成员访问运算符.*,->*:成员指针访问运算符:::域运算符sizeof:长度运算符?:条件运算符#: 预处理符号
请注意,不允许通过重载定义新的运算符
这种情况下,重载和多态的区别是,重载的内容来自 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哈!
参考资料(排名不分先后):
-
构造函数详解
-
菜鸟教程——C++教程
蒟蒻所写,有问题欢迎私信。
upd:感谢 @GavinCQTD 同志帮忙发现了个错别字的bug,已修复