Cpp_note_3

Cpp_note_3

Charles Lv7

C++笔记汇总3 类和对象

类和对象

1、封装

设计一个圆类,求圆的周长

1
2
3
4
5
6
7
8
9
#define PI 3.14
class Circle {
// 使用权限
public:
// 属性
int r;
// 行为
double calc() { return 2 * PI * r; }
};

三种权限

公共权限 public 成员类内可以访问,类外可以访问

保护权限 protected

成员类内可以访问,类外不可以访问,继承中儿子可以访问父亲权限 私有权限

private 成员类内可以访问,类外不可以访问,继承中儿子可以不访问父亲权限

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 Person {
public:
string m_Name;

protected:
string m_Car;

private:
int m_Password;

public:
void fun() {
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 123456;
}

private:
void fun2() {
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 123456;
}
};

struct 和 class 的区别

struct 默认的权限为 公共 public

class 默认的权限为 私有 private

1
2
3
4
5
6
class C1 {
int m_A;
};
struct C2 {
int m_A;
};

2.对象的初始化和清理

1. 构造函数

没有返回值

函数名和类名相同

构造函数可以有参数,可以发生重载

创建对象的时候,构造函数自动调用一次,而且仅调用一次

2. 析构函数

没有返回值

函数名和类名相同

无参数,不能发生重载

对象销毁前自动调用,且仅调用一次

构造和析构都是必须有的实现,如果我们自己不提供,编译器会自动提供一个空实现的构造和析构
1
2
3
4
5
6
7
8
9
10
class Person1 {
public:
// 构造函数
Person1() { cout << "Person1函数的调用" << endl; }
// 析构函数
~Person1() { cout << "~Person1函数的调用" << endl; }
};
void test01() {
Person1 p1;
}

构造函数的分类及调用

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 分类
class Person2 {
public:
int age;
// 普通构造函数
Person2() { cout << "Person2函数调用" << endl; }
Person2(int a) {
age = a;
cout << a << endl;
}
// 拷贝构造函数
Person2(const Person2& p) {
age = p.age;
cout << age << endl;
}

// 析构函数
~Person2() { cout << "~Person2函数调用" << endl; }
};
// 调用
void test02() {
// 1.括号法
Person2 p1;
Person2 p2(10);
// 拷贝构造函数调用
Person2 p3(p2);
// 注意事项
// 调用默认构造函数时,不要加(),否则编译器会以为是函数声明
// 2.显示法
Person2 p4 = Person2(10);
Person2 p5 = Person2(p4); // 拷贝构造
Person2(10);
// 匿名对象
// 特点:当前行结束后先调用析构函数,系统立即回收掉匿名对象,之后再继续执行后面的代码
// 注意事项
// 不要使用拷贝构造来初始化匿名对象
// Person2(p5); //编译器理解为Person p5
// 3.隐式转换法
Person2 p6 = 10; // 相当于Person2 p6=Person2(10);
}
// 拷贝构造函数调用的时机
class Person3 {
public:
int age;
Person3() { cout << "Person3函数的调用" << endl; }
Person3(int age) {
this->age = age; // this指针
cout << "Person3函数的调用" << endl;
}
Person3(Person3& p) {
this->age = p.age;
cout << "Person3函数的调用" << endl;
}
~Person3() { cout << "~Person3函数的调用" << endl; }
};
void do_work(Person3 p) {
p.age = 10;
}
Person3 do_work2() {
Person3 p(10);
return p;
}
void test03() {
// 1.使用一个已经创建完毕的对象来初始化一个新对象
Person3 p1(20);
Person3 p2(p1);
// 2.值传递的方式给函数参数传值
Person3 p4;
do_work(p4);
// 3.值方式返回局部变量
// Person3 p5 = do_work2();//按教程写的,但是会报错
}

如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造

如果用户定义拷贝构造函数,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
class Person4 {
public:
Person4() { cout << "Person4函数的调用" << endl; }
Person4(int age, int height) {
this->age = age; // 浅拷贝
this->height = new int(height); // 深拷贝
}
Person4(const Person4& p) {
// 重写拷贝构造函数
this->age = p.age;
this->height = new int(*p.height);
cout << "Person4拷贝构造函数调用" << endl;
}
~Person4() {
// 析构代码,将堆区开辟数据做释放操作
if (this->height != NULL) {
delete this->height;
this->height = NULL;
}
cout << "~Person4函数的调用" << endl;
}
int age;
int* height;
};
void test04() {
Person4 p1(18, 160);
Person4 p2(p1);
}

初始化列表

1
2
3
4
5
6
7
8
9
10
11
12
class Person5 {
public:
int a, b, c;
// 传统初始化操作
// Person5(int a, int b, int c) {
// this->a = a;
// this->b = b;
// this->c = c;
// }
// 初始化列表初始化属性
Person5(int a, int b, int c) : a(a), b(b), c(c) {}
};

Static

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
// 类对象可以作为类成员
// 静态成员变量
/* 1.所有对象都共用同一份数据
2.编译阶段就分配内存
3.类内声明,类外初始化操作 */
class Person6 {
public:
static int a; // 类内声明
private:
static int b;
};
int Person6::a = 100; // 类外初始化
int Person6::b = 200;
void test06() {
Person6 p1;
cout << p1.a << endl;
Person6 p2;
p2.a = 200; // 所有对象共用一份数据
cout << p1.a << endl;
// 静态成员变量的访问方式:
// 1.通过对象进行访问
Person6 p3;
cout << p3.a << endl;
// 2.通过类名进行访问
cout << Person6::a << endl;
// cout << Person6::b << endl;
// 私有权限类外无法访问(b)
}
// 静态成员函数
/* 1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量 */
class Person7 {
public:
static void func() {
a = 100;
// b = 200;//报错,不能访问非静态变量
cout << "static void fun的调用" << endl;
}
static int a;
int b;
// 静态成员函数也有访问权限
private:
static void fun2() { cout << "static void fun2调用" << endl; }
};
int Person7::a;
void test07() {
// 两种访问方式:
// 1.通过对象访问
Person7 p1;
p1.func();
// 2.通过类名访问
Person7::func();

// Person7::func2();//private不可访问
}
成员对象和成员函数分开存储
1
2
3
4
5
6
7
8
9
10
11
class Person1 {
int a;
static int b;
void func() {}
static void func2() {}
};
int Person1::b;
void test01() {
Person1 p;
cout << sizeof(p) << endl;
}

Summary:

1.空对象默认字节为 1,若非空则不占字节

2.非静态变量属于类的对象,占用字节空间

3.静态变量不属于类的对象,不占用字节空间

4.静态函数不属于类的对象,不占用字节空间

5.非静态函数不属于类的对象,不占用字节空间

this指针作用:

1.解决名称冲突

2.返回对象本身用*this

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
class Person2 {
public:
int age;
Person2(int age) { this->age = age; }
Person2& PersonAddAge(Person2& p) {
this->age += p.age;
return *this;
}
};
void test02() {
Person2 p1(10);
Person2 p2(20);
// 链式编程思想
p1.PersonAddAge(p2).PersonAddAge(p2);
cout << p1.age << endl;
}
// 空指针访问成员函数
class Person3 {
public:
void showClassName() { cout << "this is Person3 class" << endl; }
void showPersonAge() { cout << "age=" << age << endl; }
int age;
};
void test03() {
Person3* p = NULL;
p->showClassName();
// 可以正常运行
// p->showPersonAge();
// 报错
}

const修饰成员函数

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 Person4 {
public:
// this指针的本质是一个指针常量
// Person4 *const this;
// 在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
void showPerson() const {
this->b = 100;
// a=100;//报错
// this = NULL;//报错,this指针不可以改变指针的指向
}
void fun() { a = 10; }
int a;
mutable int b; // 特殊变量,即使在常函数也可以修改这个值
};
void test04() {
// const Person4 p;
// 在对象前加const,变成常对象[不知道为什么vscode会报错]
// p.a=10;//报错
// p.b = 200;
// 常对象只能调用常函数
// p.showPerson();
// p.fun();
// 常对象不可以调用普通成员函数,因为普通成员函数可以修改属性
}

友元

1.全局函数做友元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Building {
friend void goodGay(Building& building); // 友元

public:
string m_SittingRoom;
Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}

private:
string m_BedRoom;
};
void goodGay(Building& building) {
cout << "visit" << building.m_SittingRoom << endl;
cout << "visit" << building.m_BedRoom << endl;
}
void demo1() {
Building building;
goodGay(building);
}
2.类做友元
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
class Building2;  // 类似函数声明
class GoodGay {
public:
GoodGay();
void visit();
Building2* building;
};
class Building2 {
friend class GoodGay; // 友元
public:
Building2();
string m_SittingRoom;

private:
string m_BedRoom;
};
// 类外写成员函数,初始化
GoodGay::GoodGay() {
building = new Building2;
};
void GoodGay::visit() {
cout << "visit" << building->m_SittingRoom << endl;
cout << "visit" << building->m_BedRoom << endl;
}
Building2::Building2() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
void demo2() {
GoodGay gg;
gg.visit();
}
3.成员函数做友元
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
class Building3;  // 类似函数声明
class GoodGay1 {
public:
GoodGay1();
void visit();
void visit2();
Building3* building;
};
class Building3 {
friend void GoodGay1::visit(); // 友元

public:
Building3();
string m_SittingRoom;

private:
string m_BedRoom;
};
GoodGay1::GoodGay1() {
building = new Building3;
};
void GoodGay1::visit() {
cout << "visit" << building->m_SittingRoom << endl;
cout << "visit" << building->m_BedRoom << endl;
}
void GoodGay1::visit2() {
cout << "visit2" << building->m_SittingRoom << endl;
// cout << "visit2" << building->m_BedRoom << endl;//报错
}
Building3::Building3() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
void demo3() {
GoodGay1 gg;
gg.visit();
// gg.visit2();//报错
}

3.运算符重载

一.加法运算符重载

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 Person1 {
public:
int a, b;
// 1.通过成员函数重载
Person1 operator+(Person1& p) {
Person1 tmp;
tmp.a = this->a + p.a;
tmp.b = this->b + p.b;
return tmp;
}
};
// 2.通过全局函数重载
Person1 operator-(Person1& p1, Person1& p2) {
Person1 tmp;
tmp.a = p1.a - p2.a;
tmp.b = p1.b - p2.b;
return tmp;
}
//函数重载的版本
Person1 operator+(Person1& p1, int num) {
Person1 tmp;
tmp.a = p1.a + num;
tmp.b = p1.b + num;
return tmp;
}
void test01() {
Person1 p1;
p1.a = 10;
p1.b = 20;
Person1 p2;
p2.a = 30;
p2.b = 40;

Person1 p3 = p1 + p2;
//本质Person1 p3=p1.operator+(p2);
Person1 p4 = p1 - p2;
//本质Person1 p4=operator-(p1,p2);
cout << "p3.a:" << p3.a << "\np3.b:" << p3.b << endl;
cout << "p4.a:" << p4.a << "\np4.b:" << p4.b << endl;

//运算符重载也可以发生函数重载
Person1 p5 = p1 + 10;
cout << "p5.a:" << p5.a << "\np5.b:" << p5.b << endl;
}

二.左移运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person2 {
friend ostream& operator<<(ostream& out, Person2& p); //友元

public:
void setA(int a) { this->a = a; }
void setB(int b) { this->b = b; }

private:
int a, b;
// 利用成员函数重载左移运算符 p.operator<<(cout) 简化版本 p<<cout;
// 故不会利用成员函数重载<<运算符
};
ostream& operator<<(ostream& out, Person2& p) {
out << "p.a:" << p.a << endl;
out << "p.b:" << p.b << endl;
return out;
}
void test02() {
Person2 p;
p.setA(10);
p.setB(20);
cout << p << endl;
}

三.递增运算符重载

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 MyInteger {
friend ostream& operator<<(ostream& out, MyInteger my);
// friend MyInteger& operator++(MyInteger& my);
// friend MyInteger operator++(MyInteger& my, int);

public:
MyInteger() { num = 0; }
// 前置++
MyInteger& operator++() {
++num;
return *this;
}
//后置++
// MyInteger& operator++(int)中int为占位参数
// 可以用于区分前置后置,防止无法函数重载
MyInteger operator++(int) {
MyInteger tmp = *this;
++num;
return tmp;
}

private:
int num;
};
ostream& operator<<(ostream& out, MyInteger my) {
out << my.num;
return out;
}
/* //前置++[全局变量写法]
MyInteger& operator++(MyInteger& my) {
++my.num;
return my;
}
//后置++[全局变量写法]
MyInteger operator++(MyInteger& my, int) {
MyInteger tmp = my;
++my.num;
return tmp;
} */
void test03() {
MyInteger my;
cout << ++my << endl;
cout << my++ << endl;
}

四.赋值运算符重载

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 Person4 {
public:
Person4(int age) { this->age = new int(age); }
~Person4() {
if (this->age != NULL) {
delete this->age;
this->age = NULL;
}
} // 由于=非重载为浅拷贝,析构函数导致代码崩溃,需要赋值运算符的函数重载
// 重载
Person4& operator=(Person4& p) {
if (this->age != NULL) {
delete this->age;
this->age = NULL;
}
this->age = new int(*p.age);
return *this;
}

int* age;
};
void test04() {
Person4 p1(18);
Person4 p2(20);
Person4 p3(30);
cout << "p1年龄为" << *p1.age << endl;
cout << "p2年龄为" << *p2.age << endl;
cout << "p3年龄为" << *p3.age << endl;
p2 = p1 = p3; // 赋值操作
cout << "p1年龄为" << *p1.age << endl;
cout << "p2年龄为" << *p2.age << endl;
cout << "p3年龄为" << *p3.age << endl;
}

五.关系运算符重载

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
class Person5 {
public:
string name;
int age;
Person5(string name, int age) {
this->age = age;
this->name = name;
}
// 函数重载
bool operator==(Person5& p) {
if (this->age == p.age && this->name == p.name) {
return true;
}
return false;
}
bool operator!=(Person5& p) {
if (this->age == p.age && this->name == p.name) {
return false;
}
return true;
}
};
void test05() {
Person5 p1("Tom", 18);
Person5 p2("Tom", 18);
if (p1 == p2) {
cout << "p1==p2" << endl;
} else {
cout << "p1!=p2" << endl;
}
}

六.函数调用运算符()重载[仿函数]

1
2
3
4
5
6
7
8
9
10
11
12
class MyPrint {
public:
void operator()(string test) { cout << test << endl; }
};
void MyPrint02(string test) {
cout << test << endl;
}
void test06() {
MyPrint my;
my("hello world");
MyPrint02("hello world");
}

4.继承

继承的好处:

1.减少重复代码

2.子类也成为派生类,父类也称基类

语法:class子类:继承方式 父类
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
class BasePage {
public:
void header() { cout << "头部" << endl; }
void footer() { cout << "脚部" << endl; }
void left() { cout << "左侧" << endl; }
};
class Java : public BasePage {
public:
void content() { cout << "Java内容" << endl; }
};
class Python : public BasePage {
public:
void content() { cout << "Python内容" << endl; }
};
void test01() {
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
Python py;
py.header();
py.footer();
py.left();
py.content();
}

三种继承方式 优先级public>protected>private

公共继承

保护继承

私有继承

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
56
57
58
59
60
61
62
63
64
65
66
67
68
class Base1 {
public:
int a;

protected:
int b;

private:
int c;
};
class Son1 : public Base1 {
public:
void fun() {
a = 10; // 父类中的公共权限成员,到子类中依然是公共权限
b = 20; // 父类中的保护权限成员,到子类中依然是保护权限
// c = 30;
// 报错,父类中的私有权限成员,到子类访问不到
}
};
class Son2 : protected Base1 {
public:
void fun() {
a = 10; // 父类中的公共权限成员,到子类中是保护权限
b = 20; // 父类中的保护权限成员,到子类中是保护权限
// c = 30;
// 报错,父类中的私有权限成员,到子类访问不到
}
};
class Son3 : private Base1 {
public:
void fun() {
a = 10; // 父类中的公共权限成员,到子类中是私有权限
b = 20; // 父类中的保护权限成员,到子类中是私有权限
// c = 30;
// 报错,父类中的私有权限成员,到子类访问不到
}
};
class GrandSon3 : public Son3 {
public:
void func() {
// a=100;
// b=100;
// c=100;
// 父类不可访问或为私有,子类均不能访问
}
};
void test02() {
Son1 s1;
s1.a = 100;
// s1.b=100;
// 到Son1中b是保护权限,类外访问不到
// s1.c=100;
// 到Son1中c是无法访问的
Son2 s2;
// s2.a = 200;
// 到Son2中a是保护权限,类外访问不到
// s2.b=200;
// 到Son2中b是保护权限,类外访问不到
// s2.c=200;
// 到Son2中c是无法访问的
Son3 s3;
// s3.a = 300;
// 到Son3中a是私有权限,类外访问不到
// s3.b=300;
// 到Son3中b是私有权限,类外访问不到
// s3.c=300;
// 到Son3中c是无法访问的
}

继承中的对象模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Base3 {
public:
int a;

protected:
int b;

private:
int c;
};
class Son4 : public Base3 {
public:
int d;
};
void test03() {
cout << "sizeof(Son4):" << sizeof(Son4) << endl; // 16
// 父类中的所有非静态成员属性都会被子类继承下来
// 父类中的私有成员变量属性是被编译器隐藏了,因此访问不到,但确实被继承了
}

继承中的构造和析构顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base4 {
public:
Base4() { cout << "Base4-begin" << endl; }
~Base4() { cout << "Base4-end" << endl; }
};
class Son5 : public Base4 {
public:
Son5() { cout << "Son5-begin" << endl; }
~Son5() { cout << "Son5-end" << endl; }
};
void test04() {
// Base4 b;
// 继承中的构造和析构的顺序如下:
// 先构造父类,再构造子类,析构的顺序与构造相反
Son5 s;
}

继承中同名成员处理方式

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
56
57
58
59
60
61
62
63
64
65
class Base5 {
public:
Base5() { a = 10; }
void func() { cout << "Base5-func()" << endl; }
void func(int a) { cout << "Base5-func(" << a << ")" << endl; }
int a;
};
class Son6 : public Base5 {
public:
Son6() { a = 100; }
void func() { cout << "Son6-func()" << endl; }
int a;
};
void test05() {
// 1.同名成员属性处理方式
Son6 s;
cout << s.a << endl;
cout << s.Base5::a << endl;
// 如果通过子类对象访问父类中的同名对象,需要加作用域
// 2.同名成员函数处理方式
s.func();
s.Base5::func();
// s.func(100);//报错,若重名,子类会隐藏所有父类同名的函数
s.Base5::func(100);
// 如果通过子类对象访问父类中的同名函数,需要加作用域
}
// 继承中同名静态成员处理方式
class Base6 {
public:
Base6() { a = 10; }
static void func() { cout << "Base6-func()" << endl; }
static void func(int a) { cout << "Base6-func(" << a << ")" << endl; }
static int a;
};
int Base6::a = 10;
class Son7 : public Base6 {
public:
Son7() { a = 100; }
static void func() { cout << "Son7-func()" << endl; }
static int a;
};
int Son7::a = 100;
void test06() {
// 1.同名静态成员属性处理方式:
// (1).通过对象访问
Son7 s;
cout << s.a << endl;
cout << s.Base6::a << endl;
// (2).通过类名访问
cout << Son7::a << endl;
cout << Son7::Base6::a << endl;
// 第一个::代表通过类名访问,第二个::代表访问父类作用域下

// 2.同名静态成员函数处理方式:
// (1).通过对象访问
s.func();
s.Base6::func();
s.Base6::func(100);

// 如果通过子类对象访问父类中的同名函数,需要加作用域
// (2).通过类名访问
Son7::func();
Son7::Base6::func();
Son7::Base6::func(100);
}

多继承语法

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
class Base7 {
public:
Base7() { a = 10; }
int a;
};
class Base8 {
public:
Base8() { a = 100; }
int a;
};
class Son8 : public Base7, public Base8 {
public:
Son8() {
c = 200;
d = 300;
}
int c, d;
};
void test07() {
Son8 s;
cout << "sizeof(s)" << sizeof(s) << endl;
// 当父类中出现同名成员时,需要加作用域区分
cout << "s.Base7::a" << s.Base7::a << endl;
cout << "s.Base8::a" << s.Base8::a << endl;
}
// 菱形继承
// 定义:两个派生类继承同一个基类,又有某个类同时继承两个派生类
class Animal {
public:
int age;
};
// 利用虚继承可以解决菱形继承的问题
// Animal类成为虚基类
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class YangTuo : public Sheep, public Tuo {};
void test08() {
YangTuo y;
y.Sheep::age = 18;
y.Tuo::age = 28;
// 当出现菱形继承,两个父类拥有相同的数据,需要加作用域区分
cout << y.Sheep::age << endl;
cout << y.Tuo::age << endl;
cout << y.age << endl;
}

5.多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
public:
virtual void speak() { cout << "speaking" << endl; }
// key code
};
class Cat : public Animal {
public:
void speak() { cout << "Cat-speaking" << endl; }
};
class Dog : public Animal {
public:
void speak() { cout << "Dog-speaking" << endl; }
};
void do_speak(Animal& animal) {
//地址早绑定,在编译阶段就确定函数地址
animal.speak();
// 如果想让猫说话,需要地址晚绑定,故使用virtual关键字
}

动态多态满足条件:

1.有继承关系

2.子类重写父类的虚函数

动态多态使用

父类的指针或者引用执行子类对象

1
2
3
4
5
6
7
void test01() {
Cat cat;
Dog dog;
do_speak(cat);
do_speak(dog);
cout << sizeof(Animal); // 指针占的空间
}

纯虚函数和抽象类

纯虚函数语法:

当类中含有纯虚函数,则这个类为抽象类

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
class Base {
public:
virtual void func() = 0; // 纯虚函数
// 抽象类特点:
// 1.无法实例化对象
// 2.抽象类的子类必须重写父类的纯虚函数,否则也为抽象类
};
class Son : public Base {
public:
void func() { cout << "Son-func()" << endl; }
};
void test02() {
// Base b;
// new Base;
// 报错,抽象类无法实例化对象
Son s;
// 子类必须重写父类的纯虚函数,否则无法实例化
s.func();
Base* base = new Son;
base->func();
}
// 虚析构与纯虚析构
class Animal1 {
public:
Animal1() {}
virtual void speak() = 0;
// virtual ~Animal1() {}
// 利用虚析构可以解决父类指针释放子类对象不干净的问题

// 纯虚析构:
// 纯虚析构需要声明也需要实现
// 有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
virtual ~Animal1() = 0;
};
Animal1::~Animal1() {
cout << "纯虚析构" << endl;
}
class Cat1 : public Animal1 {
public:
Cat1(string name) { this->name = new string(name); }
void speak() { cout << *name << " Cat1-speaking" << endl; }
~Cat1() {
if (this->name != NULL) {
delete this->name;
this->name = NULL;
}
}
string* name;
};
void test03() {
Animal1* animal = new Cat1("Tom");
animal->speak();
// 父类指针在析构时候,不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄露
delete animal;
}
Summary:

1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2.如果子类没有堆区数据,可以不写为虚析构或纯虚析构

3.拥有纯虚析构函数的类是抽象类

  • Title: Cpp_note_3
  • Author: Charles
  • Created at : 2022-12-28 13:10:57
  • Updated at : 2023-02-09 09:44:20
  • Link: https://charles2530.github.io/2022/12/28/cpp-note-3/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments