本文只是用来熟悉C++的面向对象语法,前提是学过别的

内容概括

类和对象

函数可在类或结构体中直接定义,也可以只进行声明,在类或结构体外再用命名空间进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 在内部定义
class Box {
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度

double getVolume(void) {
return length * breadth * height;
}
};

// 在外部定义
class Box {
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明
double get(void);
void set( double len, double bre, double hei );
};

// 成员函数定义
double Box::get(void) {
return length * breadth * height;
}

void Box::set( double len, double bre, double hei) {
length = len;
breadth = bre;
height = hei;
}

其他成员函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 普通成员函数
bool student::set(int a)

// 构造函数
void student::student()

// 带参数(可重载)
void student::student(int a, string b)

// 析构函数,当对象超出作用域或被调用 delete 显式销毁时自动调用(常用在保证堆内存被释放)
student::~student()

// 常成员函数,只读不写,保护内部
bool student::read() const

// 静态成员函数,和别的面向对象语言类似,不依赖于某个实例
static int count()

// 拷贝构造函数
// ...

派生(继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 这儿有个父类,学生吧,经典例子了
class Student {
public:
string getName();
Student(string a);
// 这里不用private关键字,原因看后面
protected:
string name;
};

// 继承出个子类,就叫本科生吧
// public Student 代表公有派生
class UnderGraduate : public Student {
public:
// 本科生的课程属性
string course;
}

// 再继承一个,研究生
class PostGraduate : public Student {
public:
// 研究生和本科生不一样了,搞研究去了
string research;
}

这里子类没有构造函数,会调用父类的构造函数,因为继承过来了,包括父类的name字段(被继承为protect字段)。

子类可以有自己的构造函数,但是无论有没有,创建子类对象时,父类构造函数都将被调用。

类访问修饰符:public、private、protect

访问 public protected private
同一个类 yes yes yes
派生类 yes yes no
外部的类 yes no no

对于子类各种继承的特性:

因此,private里的内容都不能被继承。

现在重新写一下子类,让他有两个重载的子类构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PostGraduate : public Student {
public:
// 研究生和本科生不一样了,搞研究去了
string research;
PostGraduate();
PostGraduate(string name, string res);
}
// 无参数的子类构造函数
PostGraduate::PostGraduate() {
research = "Default research";
}

// 带参数的子类构造函数
PostGraduate::PostGraduate(string name, string res) : Student(name) {
research = res;
}

子类不能继承父类的构造函数,只能调用父类构造函数

带参数的子类构造函数:程序会先传入name参数调用父类Student有参构造函数,随后初始化自己的字段,实现了子类对象的初始化。

承上启下:相同函数名执行不同内容

  • 重载
  • 隐藏
  • 覆盖(多态)

多态

隐藏(用得比较少):子类重新定义一个同名函数(参数相不相同都一样),调用子类对象的该父类方法时会出错,即隐藏(而不是重载)。

类似于Java转型时候的问题:

父类对象指针可以指向子类对象,但是只能访问父类中的属性和方法(后面可以使用虚函数实现访问子类方法,实现真正的多态)

子类对象指针指向父类对象时,会报错(因为父类缺少子类的扩展字段或方法)

虚函数

方法前加一个virtual关键字,表示这是一个虚函数(在外部定义是不带virtual的,声明的时候加上就行了)

我们假设有三个类,Student父类,PostGraduate子类,UnderGraduate子类,三个类中都有一个相同名字的虚函数study();

此时创建一个父类指针,当指向不同类的对象时,却可以分别调用他们指向的类自己对应的study()函数。这就实现了多态(相当于子类覆盖了父类方法,但是父类又可以调用子类方法,所以和前面不是用虚函数的隐藏有区别)。

C++多态的核心,就是设置一个父类指针,它可以动态指向不同类的对象,然后动态调用他们自己的方法。

重载是编译时就决定了,多态是运行时动态决定的。

抽象类

假如有一个属于学生类的对象,那么他必然是中小学生、本科生、研究生等中的一员,不可能又是学生又不属于它们中的一种。我们可以将它看作是一种接口、一种标准,而不是一个实例,所以学生类本身的对象是不应该存在的,那么它就叫做抽象类(顾名思义)

设置抽象类依赖于纯虚函数:

1
2
3
4
5
6
7
8
9
10
11
12
//学生类定义
class Student {
public:
//抽象类也是有构造函数的
Student ();
//构造函数重载
Student (int a, string b);

//声明study方法,注意前面加了virtual还有 = 0,表明这是纯虚函数
virtual void study() = 0;
// 由此可见这里函数只被声明而没有被定义
}

只要有一个方法被设置为纯虚函数,Student就自动变为了一个抽象类

此时study()只有声明,没有定义,其具体内容靠子类的多态来实现

现在依然可以创建一个Student的指针,但不能创建Student对象了(因为它是抽象类,还有方法没有被实现,如果能创建这样的对象,调用纯虚函数时你怎么让编译器知道运行啥?)

1
2
3
4
5
6
7
8
9
10
//这里报错,不允许创建抽象类对象
Student aa;
//可以创建抽象类指针
student *p;
//创建子类对象
PostGraduate bb;
//父类指针指向子类
p = &bb;
//通过多态调用子类的study方法
p->study();

使用了抽象类后,父类Student依然可以有年龄姓名等基本成员,这些成员是所有学生都有的,但父类没有了自己的study方法,因为研究生本科生中小学生每个阶段都有自己的学习内容,所以具体study函数内容靠子类去实现。

这些内容的实现机制依赖于虚表等内容,有时间以后再总结。

参考链接:

_c++面向对象编程速成!90分钟搞定_哔哩哔哩_bilibili
_

⬆︎TOP