一. C++基础知识 1.1 基础知识 集成开发环境
编辑器、编译器、链接器、调试器
程序的运行从main函数开始而开始,结束而结束
编译器是从上到下逐行编译
在语法描述中,[]表示可选的。
C++语言集结构化编程、面向对象编程、泛型编程和函数式编程于一身,特别适合大型应用程序开发。
C++的头文件是不带.h扩展名的
C++的所有关键字都是小写的
C++11,空指针:nullptr,// C语言的空指针是NULL(0)
1.2 new/delete 内存管理 C++中利用new
操作符在堆中开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
示例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 #include "iostream" using namespace std;int *func () { int *p = new int (10 ); return p; } int main () { int *p=func (); cout << *p << endl; cout << *p << endl; cout << *p << endl; delete p; cout << *p << endl; }
示例2:开辟数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int main () { int * arr = new int [10 ]; for (int i=0 ;i<10 ;i++){ arr[i]=i+100 ; } for (int i=0 ;i<10 ;i++){ cout << arr[i] << endl; } delete [] arr; return 0 ; }
1.3 函数的默认参数 在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 #include "iostream" using namespace std;int func (int a,int b,int c) { return a+b+c; } int func2 (int a=10 ,int b=10 ,int c=10 ) ;int func2 (int a,int b,int c) { return a+b+c; } int main () { cout << func2 () << endl; return 0 ; }
1.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 #include "iostream" using namespace std;int func (int a,int b) { cout << "你好" <<endl; return 0 ; } int func (char c,char d) { cout << "hello" <<endl; return 0 ; } int main () { func ('c' ,'d' ); return 0 ; }
1.5 内联函数 1.6 引用 1.6.1基本语法 作用:给变量起别名
语法:数据类型 &别名 =原名
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include "iostream" using namespace std;int main () { int a = 10 ; int &b = a; cout << "a=" << a << endl; cout << "b=" << b << endl; b= 100 ; cout << "b=" << b << endl; cout << "a=" << a << endl; }
1.6.2引用的注意事项 引用必须初始化
引用在初始化后,不可以改变
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #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; }
1.6.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 #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 ; mySwap03 (a,b); cout << "a=" << a <<endl; cout << "b=" << b <<endl; }
1.6.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 #include "iostream" using namespace std;int & test02 () { static int a = 10 ; return a; } int main () { int &ref2=test02 (); cout << "ref2=" << ref2 << endl; cout << "ref2=" << ref2 << endl; test02 ()=1000 ; cout << "ref2=" << ref2 << endl; cout << "ref2=" << ref2 << endl; }
1.6.5 引用的本质 本质:引用的本质在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 #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 ; }
结论:c++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
二. 类与对象 C++面向对象的三大特性
:封装、继承、多态
C++认为万事万物都皆为对象,对象上有其属性和行为
例如 :
人可以作为对象,属性有姓名、年龄、身高、体重… 行为有走、跳、吃饭、唱歌…
车也可以作为属性,属性有轮胎、方向盘、车灯… 行为有载人、放音乐、开空调
具有相同性质的对象,我们可以抽象成为类,人属于人类,车属于车类
2.1封装 2.1.1封装的意义 封装是C++面向对象三大特性之一
封装的意义:
1.将属性和行为作为一个整体,表现生活中的事务
2.将属性和行为加以权限控制
2.1.1.1封装的意义一 在设计类的时候,属性和行为写在一起,表现事物
语法: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 #include "iostream" using namespace std;const double PI = 3.14 ;class Yuan { public : int m_r; double calculateZC () { return 2 * PI * m_r; } }; int main () { Yuan c1; c1.m_r=10 ; cout << "圆的周长为:" << c1.calculateZC () << 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 45 46 47 48 49 50 51 52 #include "iostream" #include "string" using namespace std;class Student {public : string Name; long long int ID; void showID () { cout << "姓名为:" << Name << "学号为:" << ID << endl; } void setName (string name) { Name=name; } void setID (long long int id) { ID=id; } }; int main () { Student s1; s1.setName ("Le1aaaaaaaaaa" ); s1.ID = 20310420203 ; s1.showID (); Student s2; s2.Name ="pl1rry" ; s2.setID (20310420107 ); s2.showID (); }
2.1.1.2封装的意义二 类在是在设计时,可以把属性和行为放在 不同的权限下,加以控制
访问权限有三种:
1.public 公共权限
2.protected 保护权限
3.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 #include "iostream" using namespace std;#include "string" class Person {public : string Name; protected : string Car; private : int Password; public : void func () { Name = "Le1a" ; Car = "法拉利" ; Password = 918223 ; cout << "姓名为" << Name << "开着是:" << Car << "银行卡密码是:" << Password << endl; } }; int main () { Person p1; p1.func (); }
2.2 成员 类中的属性和行为 统一称为 成员
2.3 访问权限 2.3.1 private 私有权限,类内可以访问,类外不能访问。父类的私有权限,子类也不能访问
2.3.2 protected 保护权限,类内可以访问,类外不能访问。继承时,父类的保护权限,子类可以访问
2.3.3 public 公共权限,类内和类外都能访问
2.3.4 C++中的struct 在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 #include "iostream" using namespace std;class C1 { int m_A; }; struct C2 { int m_A; }; int main () { C1 c1; C2 c2; c2.m_A=100 ; cout << "m_A=" << c2.m_A << endl; }
2.3.5 成员属性设为私有 优点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 44 45 46 47 48 49 50 51 52 #include "iostream" using namespace std;class Person {private : string Name; int Age; string Lover; public : void setName (string name) { Name=name; } string getName () { return Name; } int getAge () { Age = 20 ; return Age; } void setLover (string lover) { Lover=lover; } }; int main () { Person p; p.setName ("张三" ); cout << "姓名为:" << p.getName () << endl; cout << "年龄为:" << p.getAge () << endl; p.setLover ("小葵" ); }
优点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 60 61 62 63 64 65 #include "iostream" using namespace std;class Person {private : string Name; int Age; string Lover; public : void setName (string name) { Name=name; } string getName () { return Name; } int getAge () { return Age; } void setAge (int age) { if (age < 0 || age >150 ) { Age=0 ; cout << "输入年龄有误!" << endl; return ; } Age=age; } void setLover (string lover) { Lover=lover; } }; int main () { Person p; p.setName ("张三" ); cout << "姓名为:" << p.getName () << endl; p.setAge (100 ); cout << "年龄为:" << p.getAge () << endl; p.setLover ("小葵" ); }
2.3.6练习案例1:设计立方体类 设计立方体类(Cube)
求出立方体的面积和体积
分别用全局函数和成员函数判断两个立方体是否相等
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;class Cube { private : int L; int W; int H; public : void setL (int l) { L=l; } int getL () { return L; } void setW (int w) { W=w; } int getW () { return W; } void setH (int h) { H=h; } int getH () { return H; } int getS () { return 2 *L*W +2 *W*H + 2 *L*H ; } int getV () { return L*W*H; } bool isSameByClass (Cube &c) { if (L==c.getL () && W==c.getW () && H==c.getH ()){ return true ; } else { return false ; } } }; bool isSame (Cube &c1,Cube &c2) { if (c1.getL ()==c2.getL () && c1.getW ()==c2.getW () && c1.getH ()==c2.getH ()){ return true ; } else { return false ; } } int main () { Cube c1; c1.setL (10 ); c1.setW (10 ); c1.setH (10 ); cout << "立方体的面积为:" << c1.getS () << endl; cout << "立方体的体积为:" << c1.getV () << endl; Cube c2; c2.setL (10 ); c2.setW (10 ); c2.setH (10 ); bool ret =isSame (c1,c2); bool ret2=c1.isSameByClass (c2); if (ret){ cout << "c1和c2是相等的!" << endl; } else { cout << "c1和c2不相等!" << endl; } if (ret2){ cout << "用成员函数判断,c1和c2是相等的!" << endl; } else { cout << "用成员函数判断,c1和c2不相等!" << endl; } }
2.4 构造函数与析构函数 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用的时候也会自动删除一些自己信息数据保证安全
C++中的面向对象来源于生活,每个对象也会有初始设置以及对象销毁前的数据清理的设置
对象的初始化和清理也是两个非常重要的安全问题:
1.一个对象或者变量没有初始状态,对其使用后果是未知
2.同样的使用完一个对象或变量,没有及时清理,也会造成—定的安全问题
C++利用了构造函数
和析构函数
解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。
2.4.1 作用 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
析构函数: 主要作用在于对象销毁前系统自动调用,执行一些清理工作。
2.4.2构造函数和析构函数的语法 构造函数语法: 类名(){}
1.构造函数,没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数
,因此可以发生重载
4.程序在调用对象的时候会自动调用构造,无需手动调用,而且只会调用一次
析构函数语法: ~类名(){}
1.析构函数,没有返回值也不写void
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 45 #include "iostream" using namespace std;class Person {public : Person (){ cout << "Person 构造函数的调用" <<endl; } ~Person (){ cout << "Person 析构函数的调用" <<endl; } }; void test01 () { Person p; } int main () { test01 (); system ("pause" ); return 0 ; }
2.4.3 它们的四个特点
构造函数
析构函数
1
与类同名
与类同名,前面加~
2
不能有类型, void、return都不要
3
可以带参数,能够重载
没有参数,不能重载
4
一般为公有函数
基类一般采用虚析构函数
2.4.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 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 Person {public : Person (){ cout << "Person的无参构造函数调用" << endl; } Person (int a){ age=a; cout << "Person的有参构造函数调用" << endl; } Person (const Person &p){ age=p.age; cout << "Person的拷贝构造函数调用" << endl; } ~Person (){ cout << "Person的析构函数调用" << endl; } int age; }; void test01 () { Person p; } void test02 () { Person p1 (10 ) ; Person p2 (p2) ; Person p3 = Person (10 ); Person p4 = Person (p3); Person p5=10 ; Person p6=p5; } int main () { test01 (); test02 (); system ("pause" ); return 0 ; }
匿名对象,当前行执行后,立刻就被回收了,所以立刻调用了析构函数
,随后才 打印aaaaa
2.4.5 拷贝构造函数的调用时机 C++中拷贝构造函数调用的时机通常有三种情况
1.使用 一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include "iostream" using namespace std;class Person {public : Person (){ cout << "Person的默认构造函数调用" << endl; } Person (int age){ Age=age; cout << "Person的有参构造函数调用" << endl; } Person (const Person &p){ cout << "Person的拷贝构造函数调用" << endl; Age=p.Age; } ~Person (){ cout << "Person的析构函数调用" << endl; } int Age; }; void test01 () { Person p1 (10 ) ; Person p2 (p1) ; cout << "p2的年龄为:" << p2.Age << endl; } void doWork (Person p) {} void test02 () { Person p; doWork (p); } Person doWork2 () { Person p1; return p1; } void test03 () { Person p = doWork2 (); } int main () { test03 (); system ("pause" ); return 0 ; }
2.4.6初始化列表 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 : Person (int a,int b,int c):A (a),B (b),C (c){} void PrintPerson () { cout << "A=" << A <<endl; cout << "B=" << B <<endl; cout << "C=" << C <<endl; }; private : int A; int B; int C; }; void test01 () { Person p (30 ,20 ,10 ) ; p.PrintPerson (); } int main () { test01 (); system ("pause" ); return 0 ; }
这里的初始化列表来初始化属性的方法,冒号在构造函数的形参列表括号后面,然后属性(),属性(),属性()…最后{}
2.4.7默认函数 2.5类对象作为类成员 C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
例如:
1 2 3 4 class A { }class B { A a; }
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 #include "iostream" using namespace std;#include "string" class Phone {public : Phone (string Pname){ cout << "Phone的构造函数调用" << endl; PName=Pname; } ~Phone (){ cout << "Phone的析构函数调用" << endl; } string PName; }; class Person {public : Person (string name,string pname):Name (name), Phone (pname){ cout << "Person的构造函数调用" << endl; } ~Person (){ cout << "Person的析构函数调用" << endl; } string Name; Phone Phone; }; void test01 () { Person p ("张三" ,"苹果MAX" ) ; cout << p.Name << "拿着:" << p.Phone.PName << endl; } int main () { test01 (); system ("pause" ); return 0 ; }
2.6 静态成员函数 静态成员就是在成员变量和成员函数前加上关键字static
,成为静态成员
静态成员分为:
静态成员变量:
1.所有对象共享同一份数据
2.在编译阶段分配内存
3.类内声明,类外初始化
静态成员函数:
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 #include "iostream" using namespace std;class Person {public : static void func () { cout << "static void func调用" << endl; cout << "静态成员变量A的值为:" << A << endl; } static int A; int B; private : static void func2 () { cout << "static void func2的调用" << endl; } }; int Person::A=0 ;void test01 () { Person p; p.func (); Person::func (); } int main () { test01 (); }
2.7 C++对象模型和this指针 2.7.1 成员变量和成员函数分开存储 在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 #include "iostream" using namespace std;class Person { int A; static int B; void func () {} static void func2 () {} }; int Person::B=0 ;void test01 () { Person p; cout << "size of p =" << sizeof (p) << endl; } void test02 () { Person p; cout << "size of p =" << sizeof (p) << endl; } int main () { test02 (); system ("pause" ); return 0 ; }
2.7.2this指针概念
通过2.7.1我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用一块代码
那么问题是:这一块代码是如何区分哪个对象调用的自己呢?
C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
this指针是隐含
每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
1.当形参和成员变量同名时,可以用this指针来区分
2.在类的非静态成员函数中返回对象本身 ,可使用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 #include "iostream" using namespace std;class Person {public : Person (int age){ this ->age=age; } int age; Person& PersonAddAge (Person &p) { this ->age += p.age; return *this ; } }; 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).PersonAddAge (p1); cout << "p2的年龄为:" << p2.age << endl; } int main () { test02 (); system ("pause" ); return 0 ; }
2.7.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 45 #include "iostream" using namespace std;class Person {public : void showClassName () { cout << "This is Person Class" << endl; } void showPersonAge () { if (this == NULL ){ return ; } cout << "age = " << m_Age << endl; } int m_Age; }; void test01 () { Person * p =NULL ; p->showClassName (); p->showPersonAge (); } int main () { test01 (); system ("pause" ); return 0 ; }
2.7.4const修饰成员函数 2.8 友元friend 2.8.1 友元函数 生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中的私有成员
友元的关键字为 friend
友元的三种实现
1.全局函数做友元
2.类做友元
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 #include "iostream" using namespace std;#include "string" class Building { friend void goodGay (Building &building) ; public : Building (){ m_SittingRoom = "客厅" ; m_BedRoom = "卧室" ; } public : string m_SittingRoom; private : string m_BedRoom; }; void goodGay (Building &building) { cout << "好基友的全局函数 正在访问: " << building.m_SittingRoom << endl; cout << "好基友的全局函数 正在访问: " << building.m_BedRoom << endl; } void test01 () { Building building; goodGay (building); } int main () { test01 (); system ("pause" ); return 0 ; }
关键点:
这样写了之后,goodGay这个全局函数就可以访问类里面的私有成员了
2.8.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 #include "iostream" using namespace std;#include "string" 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 (); }
2.8.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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include "iostream" using namespace std;#include "string" class Building ;class GoodGay {public : GoodGay (); Building * building; void visit () ; void visit2 () ; }; 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 (); system ("pause" ); return 0 ; }
三.运算符重载 运算符重载概念: 对已有的运算符重新进行定义, 赋予其另一种功能, 以适应不同的数据类型
3.1 两种重载形式 3.3.1加号运算符重载 作用:实现两个自定义数据类型相加的运算
对于内置数据类型,编译器知道如何进行运算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person { public : int m_A; int m_B; }; Person p1; p1.m_A=10 ; p1.m_B=10 ; Person p2; p2.m_A=10 ; p2.m_B=10 ; Person p3=p1+p2;
通过自己写成员函数,实现两个对象相加属性后返回新的对象
1 2 3 4 5 6 7 Person PersonAddPerson (Person &p) { Person temp; temp.m_A = this ->m_A + p.m_A; temp.m_B = this ->m_B + p.m_B; return temp; }
这个函数呢,编译器给起了一个通用的名称
1.通过成员函数重载+号
1 2 3 4 5 6 7 8 9 10 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; } Person p3 = p1.operator +(p2); Person p3 = p1 + p2;
2.通过全局 函数重载+
1 2 3 4 5 6 7 8 9 Person operator +(Person &p1,Person &p2){ Person temp; temp.m_A = p1.m_A + p2.m_A; temp.m_B = p1.m_B + p2.m_B; return temp; } Person p3 = operator +(p1,p2); Person p3 = 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 #include "iostream" using namespace std;class Person {public : public : int m_A; int m_B; }; Person operator +(Person &p1,Person &p2){ Person temp; temp.m_A = p1.m_A + p2.m_A; temp.m_B = p1.m_B + p2.m_B; return temp; } 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=20 ; Person p2; p2.m_A=10 ; p1.m_B=20 ; Person p3 =p1 + p2; Person p4 = p3 + 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 (); }
总结1:对于内置的数据类型的表达式的 运算符 是不可能改变的
总结2:不要滥用运算符重载
四. 继承与派生 继承是面向对象三大特性之一
有些类与类之间纯在特殊的关系,例如下图中:
我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性
这个时候我们就可以考虑利用继承的技术,减少重复代码
4.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 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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 #include "iostream" using namespace std;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 << "Python学科视频" << 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 << "CPP下载视频页面如下: " << endl; CPP cpp; cpp.header (); cpp.footer (); cpp.left (); cpp.content (); } int main () { test01 (); system ("pause" ); return 0 ; }
4.2继承方式 继承方式:
默认的继承方式是私有继承,最常见的继承方式是公有继承
继承的语法: class 子类:继承方式 父类
继承方式一共有三种:
1.公共继承
2.保护继承
3.私有继承
4.3继承中构造和析构的顺序
构造函数
的调用顺序:父类->成员对象->子类
析构函数
的调用顺序:子类->成员对象->父类
五. 虚函数与多态 5.1 虚函数 5.1.1 成员函数,区分三个概念:重载、隐藏、覆盖 5.1.2 同名函数的调用原则 5.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 #include "iostream" using namespace std;class Animal {public : virtual void speak () { cout << "动物在说话" << endl; } }; class Cat :public Animal{ void speak () { cout << "小猫在说话" << endl; } }; void doSpeak (Animal &animal) { animal.speak (); } void test01 () { Cat cat; doSpeak (cat); } int main () { test01 (); }
总结:
多态满足条件
有继承关系
子类重写父类中的虚函数
多态使用条件
指针或引用指向子类对象
重写:函数返回类型
函数名
参数列表
完全一致称为重写
5.3 纯虚函数与抽象类 在多态中,通常父类中的虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数
的语法: virtual 返回值类型 函数名(参数列表) = 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 #include "iostream" using namespace std;class Base {public : virtual void func () = 0 ; }; class Son :public Base{public : virtual void func () { cout << "Son已经完成重写父类的纯虚函数" << endl; } }; void test01 () { Base * base = new Son; base->func (); } int main () { test01 (); }
5.4虚析构和纯虚析构 多态使用时 ,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构
或者纯虚析构
虚析构和和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
5.5 RTTI,运行时类型识别 运行时类型识别(Run-time type identification , RTTI) ,是指在只有一个指向父类的指针或引用时,确定所指对象的准确类型的操作。其常被说成是C++的四大扩展之一(其他三个为异常、模板和名字空间)。
使用RTTI的两种方法:
1、typeid()
第一种就像sizeof(),它看上像一个函数,但实际上它是由编译器实现的。typeid()带有一个参数,它可以是一个对象引用或指针,返回全局 typeinfo类 的常量对象的一个引用。可以用运算符“= =”和“!=”来互相比较这些对象,也可以用**name()**来获得类型的名称。如果想知道一个指针所指对象的精确类型,我们必须逆向引用这个指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class A { virtual fun () = 0 ; }; class B : public A{ void fb () {}; }; class C : public A{ void fc () {}; };
A* pa = new B; //父类指针指向子类对象
B* pb = dynamic_cast<B*>(pa);
pb->fb();
六.异常处理 异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。
异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw 。
throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 try { }catch ( ExceptionName e1 ) { }catch ( ExceptionName e2 ) { }catch ( ExceptionName eN ) { }
如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。
6.1抛出异常 您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。
以下是尝试除以零时抛出异常的实例:
1 2 3 4 5 6 7 8 double division (int a, int b) { if ( b == 0 ) { throw "Division by zero condition!" ; } return (a/b); }
6.2捕获异常 catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。
1 2 3 4 5 6 7 try { }catch ( ExceptionName e ) { }
上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …,如下所示:
1 2 3 4 5 6 7 try { }catch (...) { }
下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。
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; double division (int a, int b) { if ( b == 0 ) { throw "Division by zero condition!" ; } return (a/b); } int main () { int x = 50 ; int y = 0 ; double z = 0 ; try { z = division (x, y); cout << z << endl; }catch (const char * msg) { cerr << msg << endl; } return 0 ; }
由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:
1 Division by zero condition!
七. IO流 7.1 四个预定义流对象 包括cin、cout、cerr和clog。
7.2 文件读写 程序运行产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件<fstream>
文件类型分为两种:
1.文本文件 - 文件以文本的ASCII码形式存储在计算机中
2.二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
1.ofstream
: 只写操作
2.ifstream
: 只读操作
3.fstream
: 读写操作
7.2.1写文件 写文件步骤如下:
1.包含头文件
2.创建流对象
3.打开文件
4.写数据
5.关闭文件
文件打开方式:
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾部
ios::app 追加方式写文件
ios::trunc 如果文件存在,就先删除再创建
ios::binary 二进制方式
注意:文件打开方式可以配合使用, 利用 |
操作符
例如: 用二进制方式写文件 ios::binary | ios::out
7.2.2读文件 1.包含头文件
2.创建流对象
3.打开文件并判断文件是否打开成功
1 2 3 4 5 6 ifs.open ("文件路径" ,打开方式) if (!ifs.isopen ()){ cout << "文件打开失败" << endl; return ; }
4.读取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 第一种: char buf[1024 ] = {0 };while (ifs >> buf){ cout << buf << endl; } 第二种: char buf[1024 ] = {0 };while (ifs.getline (buf,sizeof (buf))){ cout << buf << endl; } 第三种: string buf; while (gerline (ifs,buf)){ cout << buf << endl; } 第四种: char c;while ((c = ifs.get ())!=EOF){ cout << buf << endl; }
5.关闭文件
八.模版编程
8.1 函数模板语法 函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型 可以不具体制定,用一个虚拟的类型
来代表。
语法:
1 2 3 4 5 6 template <typename T>函数声明或定义 调用方法: 1. 编译器自动推导2. 显示指定类型 例如:mySwap<int >() 在<>里直接声明变量类型
解释:
template — 声明创建模板
typename — 声明其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
示例:
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 #include "iostream" using namespace std;void swapInt (int &a,int &b) { int temp = a; a=b; b=temp; } void swapDouble (double &a,double &b) { double temp =a; a=b; b=temp; } template <typename T> void mySwap (T &a, T &b) { T temp = a; a=b; b=temp; } void test01 () { int a=10 ; int b=20 ; swapInt (a,b); double c=1.1 ; double d=2.2 ; swapDouble (c,d); cout << "c=" << c << "d=" << d << endl; } void test02 () { int a=10 ; int b=20 ; double c=1.1 ; double d=2.2 ; mySwap (a,b); cout << "a=" << a << "b=" << b << endl; mySwap<double >(c,d); cout << "c=" << c << "d=" << d << endl; } int main () { test02 (); }
8.2 类模版实例化为类 8.2.1类模板语法 类模板作用:
建立一个通用类,类中的成员 数据类型可以不具体制定,用一次虚拟的类型
来代表
语法:
解释:
template — 声明创建模板
typename — 表明其后面的符号是一种数据类型 ,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
示例:
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;#include "string" template <class NametType ,class AgeType >class Person { public : Person (NametType name,AgeType age){ this ->m_Name=name; this ->m_Age=age; } void showPerson () { cout << "name: " << this ->m_Name << "age= " << this ->m_Age << endl; } NametType m_Name; AgeType m_Age; }; void test01 () { Person<string,int > p1 ("孙悟空" ,999 ) ; p1.showPerson (); } int main () { test01 (); }
8.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 #include "iostream" using namespace std;template <class NameType ,class AgeType =int > class Person {public : Person (NameType name,AgeType age){ this ->m_Name=name; this ->m_Age=age; } void showPerson () { cout << "name: " << this ->m_Name << "age= " << this ->m_Age << endl; } NameType m_Name; AgeType m_Age; }; void test01 () { Person<string,int > p ("孙悟空" ,1000 ) ; p.showPerson (); } void test02 () { Person<string> p ("猪八戒" ,999 ) ; p.showPerson (); } int main () { test02 (); }
总结:
类模板使用只能用显示指定类型方式
类模板中的模板参数列表可以有默认参数(函数模板不能这样)
九. STL 9.1 基本概念
9.2 STL六大组件 STL大体分为六大组件,分别是:容器
、算法
、迭代器
、仿函数
、适配器(配接器)
、空间配置器
1.容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
2.算法:各种常用的算法,如sort、find、copy、for_each等
3.迭代器:扮演了各种容器与算法之间的胶合剂
4.仿函数:行为类似函数,可作为 算法的某种策略
5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
6.空间配置器:负责空间的配置与管理
vector
动态数组
list
双向链表
map
红黑树
容器分为:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法分为:
质变算法: 是指运算过程中 会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的 元素内容,例如查找、计数、遍历、寻找极值等等
9.3vector存放内置数据类型 容器: vector
算法: for_each
迭代器: vector<int>::iterator
示例:
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" #include "vector" using namespace std;#include "algorithm" void myPrint (int val) { cout << val << endl; } void test01 () { vector<int > v; v.push_back (10 ); v.push_back (20 ); v.push_back (30 ); v.push_back (40 ); vector<int >::iterator itBegin = v.begin (); vector<int >::iterator itEnd = v.end (); while (itBegin != itEnd){ cout << *itBegin << endl; itBegin++; } for_each(v.begin (),v.end (),myPrint); } int main () { test01 (); }
十. 设计模式 10.1 面向对象程序设计的思想 10.2 基本概念 10.3 单例模式 10.4 策略模式