C++

C++面向对象

Posted by BUAADreamer on 2021-04-22
Words 2k and Reading Time 8 Minutes
Viewed Times

C++的部分知识复习,为了准备考试

C++·

C++面向对象·

1.类&对象·

基本操作·

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
34
35
36
37
38
39
40
41
42
43
#include<iostream>
#类定义
class People {
//范围修饰符: 开头定义变量与函数
private:
//变量定义
double weight;
int age;
public:
//函数定义
double getWeight(void);
void setAge(int age2set);
People(); //构造函数
People(int age2set,weight2set);

};

//在类定义外定义函数 在返回值类型和函数名之间加上 类名+范围解析运算符 ::
People::People(){
weight=70;
age=20;
}
People::People(int age2set,double weight2set){
age=age2set;
weight=weight2set;
}
//构造函数另一种写法:初始化列表来初始化字段 与上一种写法等价
/*
People::People( double age2set, double weight2set): age(age2set),weight(weight2set)
{
...
}
*/
double People::getWeight(){
return weight;
}
void People::set(int age2set){
age=age2set;
}

//创建People类变量
People jack;
People xiaoming(1,2); //创建时自动调用了构造函数

析构函数·

在每次删除创建的对象时执行。类似构造函数,函数名与类名相同。只是函数名前要加一个~

这个函数不能有返回值和参数。有助于在跳出程序前释放资源

1
2
3
4
5
6
7
8
9
class Obj
{
public:
~Obj();
};
Obj::~Obj()
{
cout<<"Obj is being deleted"
}

拷贝构造函数·

特殊的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//形式
classname (const classname &obj) {
// 构造函数的主体
}
class Obj
{
public:
Obj(int len);
Obj(const Obj &obj);
private:
int *p;
};
Obj::Obj(int len)
{
p=new int;
*p=len; //构造函数
}
Obj::Obj(const Obj &obj)
{
p=new int;
*p=*obj.p; //拷贝
}
Obj obja(10);
Obj objb = obja; //调用了拷贝构造函数

友元函数·

friend 关键字对函数进行声明即可,友元函数可以访问这个类的所有成员。如:

1
2
3
4
5
6
7
8
9
10
11
class Obj
{
public:
friend void printa();
friend class ClassB; //ClassB中所有函数都成为了Obj对象的友元函数
private:
int a;
};
void printa(){
cout<<a;
}

内联函数·

加上inline 关键字即可。一种用空间换时间的方式,在编译时会在每个调用这个函数的位置放置这个函数副本,所以空间开销大

一般要使用最好行数较短,1-5行为宜,不要出现分支。

比如Max,Min等。

对象指针·

this指针·

是一个指向自身的指针,用this->变量/方法名来使用。

普通对象指针用法与this一样。

静态成员·

static 关键词来定义。

变量·

创建第一个类对象时若无其他初始化语句,所有的静态数据会清0。之后所有新创建的同类共享这个数据。

函数·

不能使用this指针。必须使用classname::fun()的形式来调用。

2.C++继承·

基本形式class A:access-specifier B;

同时,也可以多继承。如下代码所示

1
2
3
4
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};

修饰符public/protected/private,一般用public,即父类的public/protected成员都作为相同的权限被定义在子类中。其他两种则是将父类的public/protected成员作为相应的权限被定义。同时,三种定义皆不能访问到父类的private成员。

C++继承函数比较麻烦。

以下三种是不会继承的。其他符合权限限制的都可以访问。

  • 基类的构造函数析构函数拷贝构造函数
  • 基类的重载运算符
  • 基类的友元函数

这里实在太复杂,先简单罗列一下最常用的构造函数的继承使用说明

构造原则如下(这里参考了这篇博客 https://blog.csdn.net/lzbzclz/article/details/105062566):

  1. 在类的继承的过程中,如果子类没有定义构造函数,程序就会自动调用父类构造函数
  2. 如果子类定义了构造函数且父类是无参的构造函数,那么创建类的时候会自动先调用父类的构造函数,再调用子类的构造函数。
  3. 如果子类定义了构造函数且没有显示调用父类中唯一的带参构造函数,程序会报错。
  4. 调用父类构造函数的时候得用初始化父类成员对象的方式。

举例如下

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
34
35
36
37
38
39
40
41
42
43
44
//报错的代码
class A{
public:
A(int a);
private:
int a;
};
A::A(int a){
this.a=a;
}
class B: public A{
public:
B(int b);
private:
int b;
};
B::B(int b){
this.b=b;
}
A a(10);
B b(20);

//正确代码
class A{
public:
A(int a);
//在这里加一个不带参数的构造函数也可
private:
int a;
};
A::A(int a){
this.a=a;
}
class B: public A{
public:
B(int b);
private:
int b;
};
B::B(int b):A(10){
this.b=b;
}
A a(10);
B b(20);

3.重载运算符和重载函数·

函数运算符都可以进行重载

调用时编译器通过使用的参数类型定义中的进行比较,选择合适的,称为重载决策

函数重载·

1
2
3
4
5
6
7
8
9
10
11
12
class A {
public:
void print(){
cout<<"No Parameter!";
}
void print(string a) {
cout<< "p1:" << a;
}
}
A a;
A.print();
A.print1("a");

运算符重载·

关键字 operator 和其后要重载的运算符符号构成的,可以视为一个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Box {
public:
//如果在类内只需要传入一个Box参数
Box operator+(const Box& b) {
Box box;
box.a = this->a+b->a;
box.b = this->b+b->b;
return box;
}
int a;
int b;
}
//如果在类外传入两个Box参数
Box operator+(const Box& b1,const Box& b2) {
Box box;
box.a = b1->a+b2->a;
box.b = b1->b+b2->b;
return box;
}

4.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream> 
using namespace std;

class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函数
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);

// 存储矩形的地址
shape = &rec;
// 调用矩形的求面积函数 area
shape->area();

// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数 area
shape->area();

return 0;
}

上面代码中,由于基类中实定义了函数,后面两处都输出 Parent class area:

  • 这里也被称为 静态多态/静态链接,在程序执行前就已经将area函数定死了。

只需要在基类的函数定义前加关键字virtual,进行虚定义,后面输出的就是正常的各自的输出。

这时就是根据不同的指针地址的位置的 area 函数来执行了。

虚函数·
1
2
3
4
5
6
//带实现的虚函数
virtual int area() {
cout<<"Hello";
}
//纯虚函数
virtual int area() = 0;

5.C++数据抽象与封装·

  • 数据抽象:仅向用户暴露接口隐藏具体实现细节的机制。

  • 数据封装:把数据和操作数据的函数捆绑在一起的机制。

  • 数据成员和外界不需要的类内方法函数用private,外部接口方法用public

6.C++ 接口(抽象类)·

接口:类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类

这个和Java中的接口是一样的,直接用接口去定义一个实例会报错,需要具体实现

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Box
{
public:
// 纯虚函数
virtual double getVolume() = 0;
void setLength(double length) {
this->length = length;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
class BoxChild:public Box
{
public:
double getVolume(){
return this->length;
}
}

课程思维导图·