面向对象
在此之前康康引用:
① 作用:给变量起别名。
② 语法:数据类型 &别名 = 原名
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 #include <iostream> using namespace std;int main () { int a = 10 ; int & b = a; b = 100 ; cout << "a= " << a << endl; cout << "b= " << a << endl; return 0 ; } a= 100 b= 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 #include <iostream> using namespace std;int main () { int a = 10 ; int &b = a; int c = 20 ; b = c; cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; return 0 ; } a = 20 b = 20 c = 20
引用做函数参数
① 函数传参时,可以利用引用的技术让形参修饰实参。
② 可以简化指针修改实参。
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 #include <iostream> using namespace std;void mySwap01 (int a,int b) { int temp = a; a = b; b = temp; } void mySwap02 (int * a, int * b) { int temp = *a; *a = *b; *b = temp; } void mySwap03 (int &a, int &b) { int temp = a; a = b; b = temp; } int main () { int a = 10 ; int b = 20 ; mySwap01 (a, b); cout << "a = " << a << endl; cout << "b = " << b << endl; mySwap02 (&a, &b); cout << "a = " << a << endl; cout << "b = " << b << endl; mySwap03 (a, b); cout << "a = " << a << endl; cout << "b = " << b << endl; return 0 ; } a = 10 b = 20 a = 20 b = 10 a = 10 b = 20
引用做函数返回值
① 引用是可以作为函数的返回值存在的。
② 不要返回局部变量引用。
③ 函数调用可以作为左值。
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 #include <iostream> using namespace std;int & test01 () { int a = 10 ; return a; } int & test02 () { static int a = 10 ; return a; } int main () { int & ref = test02 (); cout << "ref = " << ref << endl; cout << "ref = " << ref << endl; cout << "ref = " << ref << endl; test02 () = 1000 ; cout << "ref = " << ref << endl; cout << "ref = " << ref << endl; return 0 ; } ref = 10 ref = 10 ref = 10 ref = 1000 ref = 1000
① 引用的本质在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 #include <iostream> using namespace std;void func (int & ref) { ref = 100 ; } int main () { int a = 10 ; int & ref = a; ref = 20 ; cout << "a:" << a << endl; cout << "ref:" << ref << endl; func (a); return 0 ; }
常量引用
① 作用:常量引用主要用来修饰形参,防止误操作。
② 在函数形参列表中,可以加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 25 26 27 28 29 30 31 32 #include <iostream> using namespace std;void showValue (const int & val) { cout << "val = " << val << endl; } int main () { const int & ref = 10 ; int a = 100 ; showValue (a); cout << "a = " << a << endl; return 0 ; } val = 100 a = 100
面向对象三大特性
① C++面向对象的三大特性为:封装、继承、多态。
② 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 29 30 31 32 33 34 #include <iostream> using namespace std;const double PI = 3.14 ;class Circle { public : int m_r; double calculateZC () { return 2 * PI * m_r; } }; int main () { Circle c1; c1.m_r = 10 ; cout << "圆的周长为:" << c1.calculateZC () << '\n' ; return 0 ; }
封装权限
① 类在设计时,可以把属性和行为放在不同的权限下,加以控制。
② 封装的意义二:
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 #include <iostream> using namespace std;class Person { public : string m_Name; protected : string m_Car; private : int m_Password; public : void func () { m_Name = "李四" ; m_Car = "奔驰" ; m_Password = 123 ; } }; int main () { Person p1; p1.m_Name = "李四" ; p1.func (); return 0 ; }
struct 和 class 区别
① 在C++中struct和class唯一的区别就在于默认的访问权限不同。
② 区别:
struct 默认权限为公共。
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 27 28 29 30 31 32 33 34 35 36 #include <iostream> using namespace std;class C1 { int m_A; }; struct C2 { int m_A; }; int main () { C1 c1; c1.m_A = 100 ; C2 c2; c2.m_A = 100 ; system ("pause" ); return 0 ; }
成员属性设置为私有
① 优点1:将所有成员属性设置为私有,可以自己控制读写权限。
② 优点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 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 #include <iostream> using namespace std;#include <string> class Person { public : void setName (string name) { m_Name = name; } string getName () { return m_Name; } int getAge () { m_Age = 0 ; return m_Age; } void setLover (string lover) { m_Lover = lover; } private : string m_Name; int m_Age; string m_Lover; }; int main () { Person p; p.setName ("张三" ); cout << "姓名为:" << p.getName () << endl; cout << "年龄为:" << p.getAge () << endl; p.getAge (); p.setLover ("小李" ); return 0 ; }
调用其他类
圆和点类
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 #include <iostream> using namespace std;class Point { public : void setX (int x) { m_X = x; } int getX () { return m_X; } void setY (int y) { m_Y = y; } int getY () { return m_Y; } private : int m_X; int m_Y; }; class Circle { public : void setR (int r) { m_R = r; } int getR () { return m_R; } void setCenter (Point center) { m_Center = center; } Point getCenter () { return m_Center; } private : int m_R; Point m_Center; }; void isInCircle (Circle& c, Point& p) { int distance = (c.getCenter ().getX () - p.getX ()) * (c.getCenter ().getX () - p.getX ()) + (c.getCenter ().getY () - p.getY ()) * (c.getCenter ().getY () - p.getY ()); int rDistance = c.getR () * c.getR (); if (distance == rDistance) { cout << "点在圆上" << endl; } else if (distance > rDistance) { cout << "点在圆外" << endl; } else { cout << "点在圆内" << endl; } } int main () { Circle c; c.setR (10 ); Point center; center.setX (10 ); center.setY (0 ); c.setCenter (center); Point p; p.setX (10 ); p.setY (10 ); isInCircle (c, p); return 0 ; }
头文件
point.h
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 #pragma once #include <iostream> using namespace std; class Point { public : void setX (int x) ; int getX () ; void setY (int y) ; int getY () ; private : int m_X; int m_Y; };
point.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "point.h" void Point::setX (int x) { m_X = x; } int Point::getX () { return m_X; } void Point::setY (int y) { m_Y = y; } int Point::getY () { return m_Y; }
circle.h头文件
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 #pragma once #include <iostream> using namespace std; #include "Point.h" class Circle { public : void setR (int r) ; int getR () ; void setCenter (Point center) ; Point getCenter () ; private : int m_R; Point m_Center; };
circle.cpp
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 #include "circle.h" void Circle::setR (int r) { m_R = r; } int Circle::getR () { return m_R; } void Circle::setCenter (Point center) { m_Center = center; } Point Circle::getCenter () { return m_Center; }
main.cpp
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 #include <iostream> using namespace std;#include "circle.h" #include "point.h" void isInCircle (Circle& c, Point& p) { int distance = (c.getCenter ().getX () - p.getX ()) * (c.getCenter ().getX () - p.getX ()) + (c.getCenter ().getY () - p.getY ()) * (c.getCenter ().getY () - p.getY ()); int rDistance = c.getR () * c.getR (); if (distance == rDistance) { cout << "点在圆上" << endl; } else if (distance > rDistance) { cout << "点在圆外" << endl; } else { cout << "点在圆内" << endl; } } int main () { Circle c; c.setR (10 ); Point center; center.setX (10 ); center.setY (0 ); c.setCenter (center); Point p; p.setX (10 ); p.setY (10 ); isInCircle (c, p); return 0 ; }
对象的初始化和清理:
① 对象的初始化和清理是两个非常重要的安全问题。
② 一个对象或者变量没有初始状态,对其使用后果是未知。
③
同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题。
④
C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
⑤
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。
构造函数和析构函数
构造函数和析构函数的作用
①
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
②
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数和析构函数的语法
① 构造函数语法:类名 () {}
构造函数,没有返回值也不写void。
函数名称与类名相同。
构造函数可以有参数,因此可以重载。
程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次。
② 析构函数语法:~类名(){}
析构函数,,没有返回值也不写void。
函数名称与类名相同,在名称前加上符号。
析构函数不可以有参数,因此不可以发生重载。
程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次。
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 #include <iostream> using namespace std;#include <string> class Person { public : Person () { cout << "Person 构造函数的调用" << endl; } ~Person () { cout << "Person 析构函数的调用" << endl; } }; void test01 () { Person p; } int main () { test01 (); return 0 ; } Person 构造函数的调用 Person 析构函数的调用
构造函数的分类及调用
① 两种分类方式:
按参数分为:有参构造和无参构造。
按类型分为:普通构造和拷贝构造。
② 三种调用方式:
括号法
显示法
隐式转换法
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> using namespace std;class Person { public : Person () { cout << "Person 无参构造函数的调用" << endl; } Person (int a) { age = a; cout << "Person 有参构造函数的调用" << endl; } Person ( const Person &p) { cout << "Person 拷贝构造函数的调用" << endl; age = p.age; } ~Person () { cout << "Person 析构函数的调用" << endl; } int age; }; void test01 () { Person p4 = 10 ; Person p5 = p4; } int main () { test01 (); return 0 ; } Person 有参构造函数的调用 Person 拷贝构造函数的调用 Person 析构函数的调用 Person 析构函数的调用
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 #include <iostream> using namespace std;class Person { public : Person () { cout << "Person 默认构造函数调用" << endl; } Person (int age) { m_Age = age; cout << "Person 有参构造函数调用" << endl; } Person (const Person& p) { m_Age = p.m_Age; cout << "Person 拷贝构造函数调用" << endl; } ~Person () { cout << "Person 析构函数调用" << endl; } int m_Age; }; void test01 () { Person p1 (20 ) ; Person p2 (p1) ; cout << "p2的年龄为:" << p2.m_Age << endl; } void doWork (Person p) {} void test02 () { Person p; doWork (p); } Person doWork2 () { Person p1; cout << (int *)&p1 << endl; return p1; } void test03 () { Person p = doWork2 (); cout << (int *)&p << endl; } int main () { test03 (); return 0 ; } Person 默认构造函数调用 005 DF904Person 拷贝构造函数调用 Person 析构函数调用 005 DF9FCPerson 析构函数调用
构造函数调用规则
① 默认情况下,C++编译器至少给一个类添加3个函数。
默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数,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 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 #include <iostream> using namespace std;class Person { public : Person () { cout << "Person 默认构造函数调用" << endl; } Person (int age) { m_Age = age; cout << "Person 有参构造函数调用" << endl; } Person (const Person & p) { m_Age = p.m_Age; cout << "Person 拷贝构造函数调用" << endl; } ~Person () { cout << "Person 析构函数调用" << endl; } int m_Age; }; void test01 () { Person p; p.m_Age = 18 ; Person p2 (p) ; cout << "p2的年龄:" << p2.m_Age << endl; } int main () { test01 (); return 0 ; } Person 默认构造函数调用 Person 拷贝构造函数调用 p2的年龄:18 Person 析构函数调用 Person 析构函数调用 请按任意键继续. . .
调用默认的拷贝构造函数
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 Person { public : Person () { cout << "Person 默认构造函数调用" << endl; } Person (int age) { m_Age = age; cout << "Person 有参构造函数调用" << endl; } ~Person () { cout << "Person 析构函数调用" << endl; } int m_Age; }; void test01 () { Person p; p.m_Age = 18 ; Person p2 (p) ; cout << "p2的年龄:" << p2.m_Age << endl; } int main () { test01 (); return 0 ; } Person 默认构造函数调用 p2的年龄:18 Person 析构函数调用 Person 析构函数调用
调用定义的有参构造函数
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> using namespace std;class Person { public : Person (int age) { m_Age = age; cout << "Person 有参构造函数调用" << endl; } ~Person () { cout << "Person 析构函数调用" << endl; } int m_Age; }; void test02 () { Person p; Person p2 (p) ; } int main () { test02 (); return 0 ; }5.5 .5 调用定义的拷贝构造函数#include <iostream> using namespace std;class Person { public : Person (const Person& p) { m_Age = p.m_Age; cout << "Person 拷贝构造函数调用" << endl; } ~Person () { cout << "Person 析构函数调用" << endl; } int m_Age; }; void test01 () { Person p; Person (10 ); Person p2 (p) ; cout << "p2的年龄:" << p2.m_Age << endl; } int main () { test01 (); return 0 ; }
深拷贝与浅拷贝
① 浅拷贝:简单的赋值拷贝操作。
② 深拷贝:在堆区重新申请空间,进行拷贝操作。
在堆区自己创建一份内存,可以避免堆区的内存重复释放。
③ 浅拷贝,带来的问题就是堆区的内存重复释放。
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 #include <iostream> using namespace std;class Person { public : Person () { cout << "Person的默认构造函数调用" << endl; } Person (int age,int height) { m_Age = age; m_Height = new int (height); cout << "Person的有参构造函数调用" << endl; } Person (const Person& p) { cout << "Person 拷贝构造函数调用" << endl; m_Age = p.m_Age; m_Height = new int (*p.m_Height); } ~Person () { cout << "Person的析构函数调用" << endl; if (m_Height != NULL ) { delete m_Height; } } int m_Age; int * m_Height; }; void test01 () { Person p1 (18 ,160 ) ; cout << "p1的年龄为:" << p1.m_Age << "身高为:" << * p1.m_Height << endl; Person p2 (p1) ; cout << "p2的年龄为:" << p2.m_Age << "身高为:" << * p2.m_Height << endl; } int main () { test01 (); system ("pause" ); return 0 ; } Person的有参构造函数调用 p1的年龄为:18 身高为:160 Person 拷贝构造函数调用 p2的年龄为:18 身高为:160 Person的析构函数调用 Person的析构函数调用
初始化列表
① C++提供了初始化列表语法,用来初始化属性。
② 语法:构造函数(): 属性1(值1),属性2(值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 33 34 35 36 37 38 39 #include <iostream> using namespace std;class Person { public : Person (int a, int b, int c) { m_A = a; m_B = b; m_C = c; } int m_A; int m_B; int m_C; }; void test01 () { Person p (10 , 20 , 30 ) ; cout << "m_A:" << p.m_A << endl; cout << "m_B:" << p.m_B << endl; cout << "m_C:" << p.m_C << endl; } int main () { test01 (); return 0 ; } m_A:10 m_B:20 m_C:30
灵活初始化操作
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 #include <iostream> using namespace std;class Person { public : Person (int a, int b, int c) :m_A (a), m_B (b), m_C (c) { } int m_A; int m_B; int m_C; }; void test01 () { Person p (30 , 20 , 10 ) ; cout << "m_A:" << p.m_A << endl; cout << "m_B:" << p.m_B << endl; cout << "m_C:" << p.m_C << endl; } int main () { test01 (); return 0 ; } m_A:30 m_B:20 m_C:10
类对象作为类成员
① C++类中的属性、方法称为成员。
② C++类中的成员可以是另一个类的对象,称该成员为对象成员。
③
B类中有对象A作为成员,A为对象成员,那么当创建B对象时,A与B的构造和析构的顺序是:
当其他类对象作为本类成员,构造时候先构造其他类对象,在构造自身。
当其他类对象作为本类成员,析构的顺序与构造相反,想析构自身,再析构其他类对象。
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 #include <iostream> using namespace std;class Phone { public : Phone (string pName) { cout << "Phone的构造函数调用" << endl; m_PName = pName; } ~Phone () { cout << "Phone的析构代码函数调用" << endl; } string m_PName; }; class Person { public : Person (string name, string pName) :m_Name (name), m_Phone (pName) { cout << "Person的构造函数调用" << endl; } ~Person () { cout << "Person的析构代码函数调用" << endl; } string m_Name; Phone m_Phone; }; void test01 () { Person p ("张三" , "苹果MAX" ) ; cout << p.m_Name << "m_A:" << p.m_Phone.m_PName << endl; } int main () { test01 (); system ("pause" ); return 0 ; } Phone的构造函数调用 Person的构造函数调用 张三m_A:苹果MAX Person的析构代码函数调用 Phone的析构代码函数调用
静态成员
①
静态成员就是在成员变量和成员函数前加上关键字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 56 #include <iostream> using namespace std;class Person { public : static int m_A; private : static int m_B; }; int Person::m_A = 100 ;void test01 () { Person p; cout << p.m_A << endl; Person p2; p2.m_A = 200 ; cout << p.m_A << endl; } void test02 () { Person p; cout << p.m_A << endl; cout << Person::m_A << endl; } int main () { test01 (); test02 (); } 100 200 200 200
静态成员函数
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 #include <iostream> using namespace std;class Person { public : static void func () { m_A = 100 ; cout << "static void func调用" << endl; } static int m_A; int m_B; private : static void func2 () { cout << "static void func2调用" << endl; } }; int Person::m_A = 0 ;void test01 () { Person p; p.func (); Person::func (); } int main () { test01 (); return 0 ; } static void func调用static void func调用
成员变量和成员函数分开存储
在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 #include <iostream> using namespace std;class Person01 { public : }; class Person02 { public : int m_A; static int m_B; static void func2 () {} }; int Person02::m_B = 0 ;void test01 () { Person01 p; cout << "size of p = " << sizeof (p) << endl; } void test02 () { Person02 p2; cout << "size of p2 = " << sizeof (p2) << endl; } int main () { test01 (); test02 (); return 0 ; } size of p = 1 size of p2 = 4
this指针概念
①
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用一块代码。
②
C++通过提供特殊的对象指针,this指针指向被调用的成员函数所属的对象。
③ this指针是隐含每一个非静态成员函数内的一种指针。
④ this指针不需要定义,直接使用即可。
⑤ this指针的用途:
当形参和成员变量同名时,可用this指针来区分。
在类的非静态成员函数中返回对象本身,可使用return * 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 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 #include <iostream> using namespace std;class Person { public : Person (int age) { this ->age = age; } Person& PersonAddAge (Person& p) { this ->age += p.age; return *this ; } int age; }; void test01 () { Person p1 (18 ) ; cout << "p1的年龄为:" << p1.age << endl; } void test02 () { Person p1 (10 ) ; Person p2 (10 ) ; p2.PersonAddAge (p1).PersonAddAge (p1).PersonAddAge (p1); cout << "p2的年龄为:" << p2.age << endl; } int main () { test01 (); test02 (); return 0 ; } p1的年龄为:18 p2的年龄为:40
空指针访问成员函数
①
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针。
② 如果用到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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <iostream> using namespace std;class Person { public : void showClassName () { cout << "this is Person class" << endl; } void showPersonAge () { if (this == NULL ) { return ; } cout << "age= " << this ->m_Age << endl; } int m_Age; }; void test01 () { Person* p = NULL ; p->showClassName (); p->showPersonAge (); } int main () { test01 (); return 0 ; } this is Person class
就是说注意当传入的是空指针的时候要注意判断一下this究竟是不是空
const修饰成员函数
① 常函数:
成员函数后加const后我们称这个函数为常函数。
常函数内不可以修改成员属性。
成员属性声明时加关键字mutable 后,在常函数中依然可以修改。
② 常对象:
声明对象前加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 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 #include <iostream> using namespace std;class Person { public : void showPerson () const { this ->m_B = 100 ; } void func () { m_A = 100 ; } int m_A; mutable int m_B; }; void test01 () { Person p; p.showPerson (); } void test02 () { const Person p; p.m_B = 100 ; p.showPerson (); } int main () { test01 (); return 0 ; }
友元
在程序里,有些私有属性,也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术。
友元的目的就是让一个函数或者类访问另一个类中私有成员。
友元的关键字为 friend。
友元的三种实现:
全局函数做友元。
类做友元。
成员函数做友元。
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 #include <iostream> using namespace std;class Buiding { friend void goodfriend (Buiding* buiding) ; public : Buiding () { m_SittingRoom = "客厅" ; m_BedRoom = "卧室" ; } public : string m_SittingRoom; private : string m_BedRoom; }; void goodfriend (Buiding *buiding) { cout << "好基友全局函数 正在访问:" << buiding->m_SittingRoom << endl; cout << "好基友全局函数 正在访问:" << buiding->m_BedRoom << endl; } void test01 () { Buiding building; goodfriend (&building); } int main () { test01 (); return 0 ; } 好基友全局函数 正在访问:客厅 好基友全局函数 正在访问:卧室
友元类
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 #include <iostream> using namespace std;class Building ; class GoodGay { public : GoodGay (); void visit () ; Building* building; }; class Building { friend class GoodGay ; public : Building (); public : string m_SittingRoom; private : string m_BedRoom; }; Building::Building () { m_SittingRoom = "客厅" ; m_BedRoom = "卧室" ; } GoodGay::GoodGay () { building = new Building; } void GoodGay::visit () { cout << "好基友类正在访问:" << building->m_SittingRoom << endl; cout << "好基友类正在访问:" << building->m_BedRoom << endl; } void test01 () { GoodGay gg; gg.visit (); } int main () { test01 (); return 0 ; } 好基友类正在访问:客厅 好基友类正在访问:卧室
成员函数做友元
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 73 74 #include <iostream> using namespace std;class Building ;class GoodGay { public : GoodGay (); void visit () ; void visit2 () ; Building* building; }; class Building { friend void GoodGay::visit () ; public : Building (); public : string m_SittingRoom; private : string m_BedRoom; }; Building::Building () { m_SittingRoom = "客厅" ; m_BedRoom = "卧室" ; } GoodGay::GoodGay () { building = new Building; } void GoodGay::visit () { cout << "visit 函数正在访问" << building->m_SittingRoom << endl; cout << "visit 函数正在访问" << building->m_BedRoom << endl; } void GoodGay::visit2 () { cout << "visit2 函数正在访问" << building->m_SittingRoom << endl; } void test01 () { GoodGay gg; gg.visit (); gg.visit2 (); } int main () { test01 (); return 0 ; } 输出如下: visit 函数正在访问客厅 visit 函数正在访问卧室 visit2 函数正在访问客厅
运算符重载简介
①
运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
② 对于内置的数据类型的表达式的运算符是不可能改变的。
加号运算符重载
① 加号运算符作用:实现两个自定义数据类型相加的运算。
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 73 74 75 76 77 78 79 80 #include <iostream> using namespace std;class Person { public : Person operator +(Person& p) { Person temp; temp.m_A = this ->m_A + p.m_A; temp.m_B = this ->m_B + p.m_B; return temp; } int m_A; int m_B; }; Person operator +(Person& p1, int num) { Person temp; temp.m_A = p1.m_A + num; temp.m_B = p1.m_B + num; return temp; } void test01 () { Person p1; p1.m_A = 10 ; p1.m_B = 10 ; Person p2; p2.m_A = 10 ; p2.m_B = 10 ; Person p3 = p1 + p2; Person p4 = p1 + 10 ; cout << "p3.m_A:" << p3.m_A << endl; cout << "p3.m_B:" << p3.m_B << endl; cout << "p4.m_A:" << p4.m_A << endl; cout << "p4.m_B:" << p4.m_B << endl; } int main () { test01 (); return 0 ; } p3.m_A:20 p3.m_B:20 p4.m_A:20 p4.m_B:20
左移运算符重载
① 左移运算符重载:可以输出自定义数据类型。
② 重载左移运算符配合友元可以实现自定义数据类型。
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 #include <iostream> using namespace std;class Person { friend ostream& operator <<(ostream& out, Person& p); public : Person (int a, int b) { m_A = a; m_B = b; } private : int m_A; int m_B; }; ostream & operator <<(ostream &cout, Person &p) { cout << "m_A= " << p.m_A << " m_B=" << p.m_B; return cout; } void test01 () { Person p (10 ,10 ) ; cout << p << " hello world" << endl; } int main () { test01 (); return 0 ; } m_A= 10 m_B=10 hello world
递增运算符
① 递增运算符重载:通过重载递增运算符,实现自己的整型数据。
② 前置递增返回的是引用,后置递增返回的是值。
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 73 74 75 76 77 78 79 80 81 82 83 #include <iostream> using namespace std;class MyInteger { friend ostream& operator <<(ostream& cout, MyInteger myint); public : MyInteger () { m_Num = 0 ; } MyInteger& operator ++() { m_Num++; return *this ; } MyInteger operator ++(int ) { MyInteger temp = *this ; m_Num++; return temp; } private : int m_Num; }; ostream & operator <<(ostream &cout, MyInteger myint) { cout << myint.m_Num; return cout; } void test01 () { MyInteger myint; cout << ++(++myint) << endl; cout << myint << endl; } void test02 () { MyInteger myint; cout << myint++ << endl; cout << myint << endl; } int main () { test01 (); test02 (); system ("pause" ); return 0 ; }
赋值运算符
① C++编译器至少给一个类添加4个函数:
默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对属性进行值拷贝
赋值运算符operator=,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现浅拷贝问题。
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <iostream> using namespace std;class Person { public : Person (int age) { m_Age = new int (age); } ~Person () { if (m_Age != NULL ) { delete m_Age; m_Age = NULL ; } } Person& operator =(Person& p) { if (m_Age != NULL ) { delete m_Age; m_Age = NULL ; } m_Age = new int (*p.m_Age); return *this ; } int *m_Age; }; void test01 () { Person p1 (18 ) ; Person p2 (20 ) ; Person p3 (23 ) ; p3 = p2 = p1; cout << "p1的年龄为:" << *p1.m_Age << endl; cout << "p2的年龄为:" << *p2.m_Age << endl; cout << "p3的年龄为:" << *p3.m_Age << endl; } int main () { test01 (); return 0 ; } p1的年龄为:18 p2的年龄为:18 p3的年龄为:18
关系重载运算符
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 73 74 75 76 #include <iostream> using namespace std;#include <string> class Person { public : Person (string name, int age) { m_Name = name; m_Age = age; } bool operator ==(Person& p) { if (this ->m_Name == p.m_Name && this ->m_Age == p.m_Age) { return true ; } return false ; } bool operator !=(Person& p) { if (this ->m_Name != p.m_Name && this ->m_Age != p.m_Age) { return true ; } return false ; } string m_Name; int m_Age; }; void test01 () { Person p1 ("Tom" , 17 ) ; Person p2 ("Jerry" , 18 ) ; if (p1 == p2) { cout << "p1和p2是相等的!" << endl; } else { cout << "p1和p2是不相等的!" << endl; } if (p1 != p2) { cout << "p1和p2是不相等的!" << endl; } else { cout << "p1和p2是相等的!" << endl; } } int main () { test01 (); return 0 ; } p1和p2是不相等的! p1和p2是不相等的!
函数调用运算符重载
① 函数调用运算符()也可以重载。
② 由于重载后使用的方式非常像函数的调用,因此称为仿函数。
③ 仿函数没有固定写法,非常灵活。
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 #include <iostream> using namespace std;#include <string> class MyPrint { public : void operator () (string test) { cout << test << endl; } }; void MyPrint02 (string test) { cout << test << endl; } void test01 () { MyPrint myPrint; myPrint ("hello world" ); } class MyAdd { public : int operator () (int num1, int num2) { return num1 + num2; } }; void test02 () { MyAdd myadd; int ret = myadd (100 ,100 ); cout << "ret = " << ret << endl; cout << MyAdd ()(100 , 100 ) << endl; } int main () { test01 (); MyPrint02 ("hello world" ); test02 (); return 0 ; } hello world hello world ret = 200 200
继承
① 继承是面向对象的三大特性之一。
②
定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性。这个时候,就可以考虑利用继承技术,减少重复代码。
普通实现
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 #include <iostream> using namespace std;#include <string> class Java { public : void header () { cout << "首页、公开课、登陆、注册...(公共头部)" << endl; } void footer () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java、Python、C++....(公共分类列表)" << endl; } void content () { cout << "Java学科视频" << endl; } }; class Python { public : void header () { cout << "首页、公开课、登陆、注册...(公共头部)" << endl; } void footer () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java、Python、C++....(公共分类列表)" << endl; } void content () { cout << "Python学科视频" << endl; } }; class CPP { public : void header () { cout << "首页、公开课、登陆、注册...(公共头部)" << endl; } void footer () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java、Python、C++....(公共分类列表)" << endl; } void content () { cout << "C++学科视频" << endl; } }; void test01 () { cout << "Java下载视频页面如下:" << endl; Java ja; ja.header (); ja.footer (); ja.left (); ja.content (); cout << "........................" << endl; cout << "Python下载视频页面如下:" << endl; Python py; py.header (); py.footer (); py.left (); py.content (); cout << "........................" << endl; cout << "C++下载视频页面如下:" << endl; CPP cpp; cpp.header (); cpp.footer (); cpp.left (); cpp.content (); } int main () { test01 (); return 0 ; } Java下载视频页面如下: 首页、公开课、登陆、注册...(公共头部) 帮助中心、交流合作、站内地图...(公共底部) Java、Python、C++....(公共分类列表) Java学科视频 Python下载视频页面如下: 首页、公开课、登陆、注册...(公共头部) 帮助中心、交流合作、站内地图...(公共底部) Java、Python、C++....(公共分类列表) Python学科视频 C++下载视频页面如下: 首页、公开课、登陆、注册...(公共头部) 帮助中心、交流合作、站内地图...(公共底部) Java、Python、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 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> using namespace std;#include <string> class BasePage { public : void header () { cout << "首页、公开课、登陆、注册...(公共头部)" << endl; } void footer () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java、Python、C++....(公共分类列表)" << endl; } }; class Java :public BasePage { public : void content () { cout << "Java学科视频" << endl; } }; class Python :public BasePage { public : void content () { cout << "Python学科视频" << endl; } }; class CPP :public BasePage { public : void content () { cout << "CPP学科视频" << endl; } }; void test01 () { cout << "Java下载视频页面如下:" << endl; Java ja; ja.header (); ja.footer (); ja.left (); ja.content (); cout << "........................" << endl; cout << "Python下载视频页面如下:" << endl; Python py; py.header (); py.footer (); py.left (); py.content (); cout << "........................" << endl; cout << "C++下载视频页面如下:" << endl; CPP cpp; cpp.header (); cpp.footer (); cpp.left (); cpp.content (); } int main () { test01 (); return 0 ; } 运行结果一致
三种继承改变权限
① 继承的语法: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 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 73 74 75 76 77 78 79 80 81 #include <iostream> using namespace std;#include <string> class Base1 { public : int m_A; protected : int m_B; private : int m_C; }; class Son1 :public Base1{ public : void func () { m_A = 10 ; m_B = 10 ; } }; void test01 () { Son1 s1; s1.m_A = 100 ; } class Son2 :protected Base1{ public : void func () { m_A = 10 ; m_B = 10 ; } }; void test02 () { Son2 s2; } class Son3 :private Base1{ public : void func () { m_A = 10 ; m_B = 10 ; } }; void test03 () { Son3 s3; } int main () { return 0 ; }
继承对象内存
** 查询继承对象所占内存**
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 #include <iostream> using namespace std;#include <string> class Base { public : int m_A; protected : int m_B; private : int m_C; }; class Son :public Base{ int m_D; }; void test01 () { cout << "size of Son =" << sizeof (Son) << endl; } int main () { test01 (); return 0 ; } size of Son =16
继承构造和析构顺序
①
继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造顺序相反,先调用子类析构函数,再调用父类析构函数。
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 #include <iostream> using namespace std;#include <string> class Base { public : Base () { cout << "Base构造函数!" << endl; } ~Base () { cout << "Base析构函数!" << endl; } }; class Son :public Base{ public : Son () { cout << "Son构造函数!" << endl; } ~Son () { cout << "Son析构函数!" << endl; } }; void test01 () { Son s; } int main () { test01 (); return 0 ; } Base构造函数! Son构造函数! Son析构函数! Base析构函数!
同名成员处理
① 子类对象可以直接访问到子类中同名成员。
② 子类对象加作用域可以访问到父类同名成员。
③
当子类与父类拥有同名的成员函数,子类会隐藏父类中所有同名成员函数(有参、无参),加作用域才可以访问到父类中同名函数。
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 73 74 75 76 77 78 79 80 81 82 83 84 #include <iostream> using namespace std;#include <string> class Base { public : Base () { m_A = 100 ; } int m_A; void func () { cout << "Base - func()调用" << endl; } void func (int a) { cout << "Base - func(int a)调用" << endl; } }; class Son :public Base{ public : Son () { m_A = 200 ; } void func () { cout << "Son - func()调用" << endl; } int m_A; }; void test01 () { Son s; cout << "Son 下 m_A=" << s.m_A << endl; cout << "Base 下 m_A=" << s.Base::m_A << endl; } void test02 () { Son s; s.func (); s.Base::func (); s.Base::func (100 ); } int main () { test01 (); test02 (); system ("pause" ); return 0 ; } Son 下 m_A=200 Base 下 m_A=100 Son - func ()调用 Base - func ()调用 Base - func (int a)调用 请按任意键继续. . .
同名静态成员处理
① 静态成员和非静态成员出现同名,处理方式一致:
访问子类同名成员,直接访问
访问父类同名成员,需要加作用域
② 加上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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 #include <iostream> using namespace std;class Base { public : static int m_A; static void func () { cout << "Base - static func()" << endl; } static void func (int a) { cout << "Base - static func(int a)" << endl; } }; int Base::m_A=100 ;class Son :public Base{ public : static int m_A; static void func () { cout << "Son - static void func()" << endl; } }; int Son::m_A = 200 ;void test01 () { Son s; cout << "Son 下 m_A = " << s.m_A << endl; cout << "Base 下 m_A = " << s.Base::m_A << endl; cout << "Son 下 m_A = " << Son::m_A << endl; cout << "Base 下 m_A= " << Son::Base::m_A << endl; } void test02 () { Son s; s.func (); s.Base::func (); Son::func (); Son::Base::func (); Son::Base::func (100 ); } int main () { test01 (); test02 (); return 0 ; } Son 下 m_A = 200 Base 下 m_A = 100 Son 下 m_A = 200 Base 下 m_A= 100 Son - static void func () Base - static func () Son - static void func () Base - static func () Base - static func (int a)
多继承语法
① C++运行一个类继承多个类。
② 语法:class 子类:继承方式 父类1,继承方式 父类2,.....
③ 多继承可能会引发父类中有同名成员出现,需要加作用域区分。
④ 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 56 57 58 59 #include <iostream> using namespace std;class Base1 { public : Base1 () { m_A = 100 ; } int m_A; }; class Base2 { public : Base2 () { m_A = 200 ; } int m_A; }; class Son :public Base1,public Base2{ public : Son () { m_C = 300 ; m_D = 400 ; } int m_C; int m_D; }; void test01 () { Son s; cout << "sizeof(Son):" << sizeof (s) << endl; cout << "Base1::m_A = " << s.Base1::m_A << endl; cout << "Base2::m_A = " << s.Base2::m_A << endl; } int main () { test01 (); return 0 ; } sizeof (Son):16 Base1::m_A = 100 Base2::m_A = 200
菱形继承简介
① 菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承两个派生类
这种继承被称为菱形继承
②
羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时候就会产生二义性。
③
草泥马继承自动物的数据继承了两份,其实我们应当清楚,这份数据我们只需要一份就可以。
④
菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义。
⑤ 利用虚继承可以解决菱形继承问题。
菱形继承普通方式
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 #include <iostream> using namespace std;class Animal { public : int m_Age; }; class Sheep :public Animal{};class Tuo :public Animal{};class SheepTuo :public Sheep,public Tuo{};void test01 () { SheepTuo st; st.Sheep::m_Age = 18 ; st.Tuo::m_Age = 28 ; cout << "st.Sheep::m_Age=" << st.Sheep::m_Age << endl; cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl; } int main () { test01 (); return 0 ; } st.Sheep::m_Age=18 st.Tuo::m_Age=28
菱形继承虚继承方式
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 #include <iostream> using namespace std;class Animal { public : int m_Age; }; class Sheep :virtual public Animal{};class Tuo :virtual public Animal{};class SheepTuo :public Sheep,public Tuo{};void test01 () { SheepTuo st; st.Sheep::m_Age = 18 ; st.Tuo::m_Age = 28 ; cout << "st.Sheep::m_Age=" << st.Sheep::m_Age << endl; cout << "st.Tuo::m_Age=" << st.Tuo::m_Age << endl; cout << "st.m_Age=" << st.m_Age << endl; } int main () { test01 (); return 0 ; } st.Sheep::m_Age=28 st.Tuo::m_Age=28 st.m_Age=28
多态简介
① 多态是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 #include <iostream> using namespace std;class Animal { public : void speak () { cout << "动物在说话" << endl; } }; class Cat :public Animal{ public : void speak () { cout << "小猫在说话" << endl; } }; void doSpeak (Animal &animal) { animal.speak (); } void test01 () { Cat cat; doSpeak (cat); } int main () { test01 (); return 0 ; } 动物在说话
地址晚绑定虚函数
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 #include <iostream> using namespace std;class Animal { public : virtual void speak () { cout << "动物在说话" << endl; } }; class Cat :public Animal{ public : void speak () { cout << "小猫在说话" << endl; } }; class Dog :public Animal{ public : virtual void speak () { cout << "小狗在说话" << endl; } }; void doSpeak (Animal &animal) { animal.speak (); } void test01 () { Cat cat; doSpeak (cat); Dog dog; doSpeak (dog); } int main () { test01 (); return 0 ; } 小猫在说话 小狗在说话
原理剖析
① 当没有发生重写时,子类中的虚函数表内部为父类的虚函数地址。
②
当子类重写父类的虚函数,子类中的虚函数表内部会替换成子类的虚函数地址。
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 73 74 75 #include <iostream> using namespace std;class Animal { public : virtual void speak () { cout << "动物在说话" << endl; } }; class Cat :public Animal{ public : void speak () { cout << "小猫在说话" << endl; } }; class Dog :public Animal{ public : virtual void speak () { cout << "小狗在说话" << endl; } }; void doSpeak (Animal &animal) { animal.speak (); } void test01 () { Cat cat; doSpeak (cat); Dog dog; doSpeak (dog); } void test02 () { cout << "sizeof Animal = " << sizeof (Animal) << endl; } int main () { test02 (); return 0 ; } sizeof Animal = 4
纯虚函数和抽象类
①
在多态中,通常父类中虚函数的实现时毫无意义的,主要都是调用子类重写的内容。因此,可以将虚函数改为纯虚函数。
② 纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;
③ 当类中有了纯虚函数,这个类也称为抽象类。
④ 抽象类特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类。
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 #include <iostream> using namespace std;class Base { public : virtual void func () = 0 ; }; class Son : public Base{ public : virtual void func () { cout << "func函数调用" << endl; } }; void test01 () { Base* base = new Son; base->func (); } int main () { test01 (); return 0 ; } func函数调用
虚析构和纯虚析构
①
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码。
② 解决方式:将父类中的析构函数改为虚析构或者纯虚析构。
③ 虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
④ 虚析构语法:virtual.类名(){}
⑤ 纯虚析构语法:
virtual~类名 = 0;
类名::~类名(){}
⑦ 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象。
⑧ 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构。
⑨ 拥有纯虚析构函数的类也属于抽象类。
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 73 74 75 76 77 78 79 80 81 82 #include <iostream> using namespace std;#include <string> class Animal { public : Animal () { cout << "Animal构造函数调用" << endl; } virtual void speak () = 0 ; virtual ~Animal () = 0 ; }; Animal::~Animal () { cout << "Animal纯虚析构函数调用" << endl; } class Cat : public Animal{ public : Cat (string name) { cout << "Cat构造函数调用" << endl; m_Name = new string (name); } virtual void speak () { cout << *m_Name << "小猫在说话" << endl; } ~Cat () { if (m_Name != NULL ) { cout << "Cat析构函数调用" << endl; delete m_Name; m_Name = NULL ; } } string* m_Name; }; void test01 () { Animal* animal = new Cat ("Tom" ); animal->speak (); delete animal; } int main () { test01 (); return 0 ; } Animal构造函数调用 Cat构造函数调用 Tom小猫在说话 Cat析构函数调用 Animal纯虚析构函数调用