一、绪论
C++是C语言的拓充,C++包含了C所有的属性,换一句话说,C语言在C++中都合法。
C++的编程思想:面向对象
,(半面向对象,半面向过程)
可以说在C++\中一切皆对象
C++的三大属性:封装
、继承
、多态
C++对C的兼容
1> C语言文件
编写一个C语言文件:XXX.c 编译:gcc XXX.c
2> C++语言文件
编写一个C++文件:XXX.cpp 编译:g++ XXX.cpp
3> 头文件
C: 以.h结尾的文件 eg:#include<stdio.h>
C++: 不以.h结尾 eg:#include
二、第一条语句
cout
cout是输出类的类对象,具有输出功能,可以自动识别数据类型,无需加格式符。
<< 插入符(输出符)
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 #include <iostream> using namespace std; int main () { cout << "Hello World!" << endl; cout << "今天是我学习C++的第一天,好开心~" << endl << 10 << endl; return 0 ; }
试编程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std; int main () { for (int i=0 ; i<10 ; i++) { int j=0 ; for (; j<=i; j++) { cout << "*" ; } cout << endl; } }
cin
cin 是输入类的类对象,具有输入功能,可以自动识别数据类型,无需加格式符
>> 提取符(输入符)
不需要给变量加地址符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> using namespace std; int main () { cout << "请输入你的年纪:" << endl; int age; cin >> age; cout << "age = " << age << endl; return 0 ; }
三、C++中的数据类型
C++的数据类型:基本数据类型、构造数据类型
基本数据类型:char、short、int、long、float、double、bool、string
构造数据类型:数组、指针、结构体、共用体、枚举、类
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> #include <iomanip> using namespace std ; int main () { char a = 'A' ; cout << "a = " << a << endl ; cout << "a = " << int (a) << endl ; cout << "=============================" << endl ; int b=100 , b2=0b100 , b3=0100 , b4=0x100 ; cout << b << endl ; cout << oct << b << endl ; cout << dec << b << endl ; cout << hex << b << endl ; cout << b << endl ; cout << dec << b << endl ; cout << "=============================" << endl ; double d1 = 12.3456789 , d2=1.23456789 ; cout << d1 << endl ; cout << d2 << endl ; cout << setprecision(4 ) << d1 << endl ; cout << setprecision(5 ) << fixed << d1 << endl ; return 0 ; }
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 #include <iostream> using namespace std ; int main () { string str1; string str2 = "hello world" ; cout << str2 << endl ; string str3 = str2; cout << str3 << endl ; string str4 (str3) ; cout << str4 << endl ; str1 = "hello kitty" ; string str5 (str1,3 ) ; cout << str5 << endl ; char *p = (char *)"abcdfdjgk" ; return 0 ; }
2. 字符串中常用的函数
size(): 元素个数
empty(): //判断字符串是否为空,如果为空放回true,否则false
capacity(): //计算容量大小
1 2 3 4 5 6 if (!str5.empty ()){ cout << str5.size () << endl; cout << str5.capacity () << endl; }
3. C++中的字符串和C语言中字符串的风格互换
1> C语言风格的字符串可以直接转换成C++风格的字符串
2> C++风格的字符串不可以直接转换成C语言风格的字符串
1 2 3 4 5 6 7 8 9 10 11 char a[20 ] = "world" ;string b = "hello" ; b = a; cout << b << endl; string c = "kitty" ; strcpy (a, c.c_str ());
4. 字符串元素的访问
1> 下标 -------->不判断是否越界
2> at() ----------->判断是否越界
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> #include <string.h> using namespace std; int main () { string c2 = "abde" ; cout << c2[6 ] << endl; cout << c2.at (2 ) << endl; cout << c2.at (8 ) << endl; return 0 ; }
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 25 26 27 28 29 30 #include <iostream> using namespace std; int main () { string str1="abc" ; string str2="ABCDEFGHIJKKKKKKKKKKKKKK" ; if (str1!=str2) { if (str1>str2) { cout << "str1 > str2" << endl; } else { cout << "str1 < str2" << endl; } } else { cout << "str1 == str2" << endl; } return 0 ; }
6. 字符串的输入
1 2 3 4 5 6 7 8 9 10 string userName; getline (cin,userName); cout << userName << endl;
四、C++数组array
需要包含头文件 #include
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 #include <iostream> #include <array> using namespace std; int main () { array< array<int ,2>, 3> a; array< array<int ,2>, 3>::iterator iter1; array<int , 2>::iterator iter2; for (iter1 = a.begin (); iter1 != a.end (); iter1++) { for (iter2 = (*iter1).begin (); iter2 != (*iter1).end (); iter2++) { cin >> *iter2; } } for (iter1 = a.begin (); iter1 != a.end (); iter1++) { for (iter2 = (*iter1).begin (); iter2 != (*iter1).end (); iter2++) { cout << *iter2 << " " ; } cout << endl; } return 0 ; }
五、命名空间
1> 多人协同开发,避免命名污染(作用)
六、引用
1概念
引用就是个别名。
2 格式
数据类型 &引用名 = 同类型的变量名 (& 引用符号)
1 2 3 4 数据类型 &引用名 = 同类型的变量名 eg: int a = 10 ; int &b = a;
案例 :
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; int main () { int a = 10 , b = 20 ; cout << "a = " << a << endl; int &aa = a; cout << "aa = " << aa << endl; aa = 30 ; cout << "a = " << a << endl; cout << "&a = " << &a << endl; cout << "&aa = " << &aa << endl; int *p; p = &a; p = &b; return 0 ; }
3 数组的引用
3.1 概念
给数组取个别名
3.2 实例
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[5 ] = {1 ,2 ,3 ,4 ,5 }; int (*p)[5 ] = &a; int (&b)[5 ] = a; cout << a[3 ] << endl; cout << b[3 ] << endl; b[4 ] = 99 ; cout << a[4 ] << endl; return 0 ; }
4 函数的引用
4.1 概念
给函数取个别名
4.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 #include <iostream> using namespace std; int max (int x, int y) { return x>y?x:y; } int main () { int (*p)(int , int ) = max; cout << p (3 ,5 ) << endl; int (&fun)(int , int ) = max; cout << fun (6 ,8 ) << endl; return 0 ; }
5 结构体中有引用成员
当结构体中有引用成员的话,使用该结构体类型定义变量时,就必须定义的同时初始化,否则报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std; struct student { int &id; string name; }; int main () { int num = 1001 ; struct student stu1 = {num, "张三" }; return 0 ; }
6 当引用作为函数的返回值
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 #include <iostream> using namespace std; int &fun () { static int num = 10 ; return num; } int main () { int a; a = fun (); cout << a << endl; return 0 ; }
7 传值、传址、传引用
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 #include <iostream> using namespace std; void fun (int a, int b) { a++; b++; } void fun2 (int *a, int *b) { *a++; *b++; } void fun3 (int &a, int &b) { int c = a; } int main () { int a = 10 , b = 20 ; fun (a,b); cout << "main: a = " << a << " b = " << b << endl; fun2 (&a, &b); cout << "main: a = " << a << " b = " << b << endl; fun3 (a,b); cout << "main: a = " << a << " b = " << b << endl; return 0 ; }
总结:指针与引用的区别(笔试、面试重点)
指针:存放地址的变量
引用:引用就是个别名
1> 指针可以先定义后指向,而引用必须定义的同时初始化。
2> 指针后期可以改变指向,而引用一旦指定目标,就不能发生变化。
3> 指针定义需要申请空间,而引用不需要申请空间。
4> 有指针数组,没有引用数组(原因:引用不是数据类型 )
5> 有二级指针,没有二级引用(原因:引用不是数据类型)
七、const
修饰变量时,表示该变量是常变量,其值不能被修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int *p;int * const p; int const *p; int const * const p; int a = 10 ;int const b = 20 ; int *pa = &a; int *pb = &b; int const *pbb = &b; int a = 10 ;int const b = 20 ;int &ppa = a; int &ppb = b; int const &pppb = b;
八、函数重载
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 #include <iostream> using namespace std; int fun () { return 5 +5 ; } int fun (int x) { return x+10 ; } int fun (int x, int y) { return x+y; } int fun (int x, string y) { return x; } int main () { cout << fun () << endl; cout << fun (10 ) << endl; 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 #include <iostream> using namespace std; int add (int x, int y) { return x+y; } int add (char x, char y) { return x+y; } string add (string x, string y) { return x+y; } double add (double x, double y) { return x+y; } int main () { cout << add ("hello" ,"world" ) << endl; cout << add (12.34 , 45.7 ) << endl; return 0 ; }
2 默认参数的函数定义和使用
1 2 3 4 void fun (string name = "zhangsan" ) { cout << name << endl; }
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 #include <iostream> using namespace std; int fun (int m, int ,int k) { return m+k; } int main () { cout << fun (1 ,2 ,3 ) << endl; cout << fun (1 ,2 ,3 ) << endl; cout << fun (1 ,2 ,3 ) << endl; cout << fun (1 ,2 ,3 ) << endl; cout << fun (1 ,2 ,3 ) << endl; cout << fun (1 ,2 ,3 ) << endl; return 0 ; }
4内联函数
内联函数就是在函数定义前 加 inline 关键字 。
要求:
1> 内联函数体积要小
2> 一般代码不超过5行
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 #include <iostream> using namespace std; #define MAX(x,y) x>y?x:y inline int max (int x, int y) { return x>y?x:y; } int main () { int m = 1 ; int n = 1 ; int c = max (++m,n); cout << "m = " << m << " n = " << n << " c = " << c << endl; m = 1 ; n = 1 ; c = MAX (++m,n); cout << "m = " << m << " n = " << n << " c = " << c << endl; return 0 ; }
九、C++中的结构体
C语言中的结构体和C++中结构体的区别
1> C语言中的结构体在C++中依然适用。
2> C++中的结构体可以有函数,而C语言不可以。
3> C++中的结构体可以给变量赋初始值,而C语言中不可以。
4> C++中的结构体在定义结构体变量时,可以省略关键字struct不写,而C语言中不可以。
5> C++中的结构体中有访问权限,而C语言中的结构体没有访问权限。
6> C++中的结构体有特殊的成员函数,而C语言中的结构体没有。
7> C++中的结构体存在继承,而C语言中的结构体没有继承。
C++中的结构名一般首字母大写。
C++结构体的默认访问权限是共有的,—>public
访问权限:private: 私有权限 protected:保护权限 public:共有权限
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; struct Student { int id = 1001 ; string name; double score; void show () { cout << "姓名: " << name << endl; cout << "学号:" << id << endl; cout << "成绩:" << score << endl; } }; int main () { Student s1; s1.name = "张三" ; s1.score = 99 ; s1.show (); return 0 ; }
十、类
1 相关概念
一般又有变量又有函数的构造数据类型,有类来完成,C++中的类是由C++中的结构体演变而来,只是默认访问权限 和默认继承方式 以及关键字 不同。
默认访问权限: private 私有的
默认继承方式:private 私有的
关键字:class
类内都可以成为成员,成员可以分为数据成员、成员函数
2 格式
1 2 3 4 5 6 7 8 9 class 类名{ public : 公共的数据成员、成员函数 protected : 受保护的数据成员、成员函数 private : 私有的数据成员、成员函数 };
3 访问权限的介绍
public: 该权限是公共权限,表示该权限下的属性(变量)、方法(函数),可以在类内、子类、类外被访问。
protected: 该权限是受保护权限,表该权限下的属性(变量)、方法(函数),可以在类内、子类被访问,类外不可以被访问。
private: 该权限是私有权限,表示该权限下的属性(变量)、方法(函数),只能在类内被访问,子类、类外不可以被访问。
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 #include <iostream> using namespace std; class Stu { public : int id; protected : string name; private : double score; public : void show () { cout << id << endl; cout << name << endl; cout << score << endl; } void inint (int i, string n, double s) { id = i; name = n; score = s; } }; int main () { Stu s1; s1.id = 1001 ; s1.inint (1002 ,"张三" ,99 ); s1.show (); return 0 ; }
十一、C++中动态内存分配和回收(堆区)
1> C语言中动态内存分配和回收使用的是malloc和free函数。
2> C++依然可以使用上述两个函数 来完成。
3> C++也为用户提供了两个关键字 new、delete来进行动态内存分配和回收。
1分配
1.1单个内存分配
格式: 数据类型 *指针名 = new 数据类型
1 2 3 eg: int *p1 = new int ;
1.2连续内存分配
格式: 数据类型 *指针名 = new 数据类型[个数]
1 2 3 eg: int *p2 = new int [5 ];
2回收
2.1单个内存回收(释放)
格式:delete 指针名;
eg: delete p1;
2.2连续内存回收
格式:delete []指针名
eg: delete []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 #include <iostream> using namespace std; int main () { int *p1 = new int ; cout << *p1 << endl; int *p2 = new int (10 ); cout << *p2 << endl; char *p3 = new char ('A' ); cout << *p3 << endl; cout << "=====================" << endl; int *p4 = new int [5 ]; for (int i=0 ; i<5 ; i++) { cout << p4[i] << endl; } cout << "=====================" << endl; int *p5 = new int [5 ]{100 ,200 ,300 ,400 ,500 }; for (int i=0 ; i<5 ; i++) { cout << p5[i] << endl; } delete p1; delete p2; delete p3; delete []p4; delete []p5; p1 = nullptr ; p2 = nullptr ; p3 = nullptr ; p4 = nullptr ; p5 = nullptr ; return 0 ; }
3 new、delete和malloc、free之间的区别(面试、笔试)
1> new、delete是关键字,而malloc、free是函数
2> new申请空间时可以初始化,而malloc不可以。
3> new申请空间以数据类型为单位,而malloc以字节为单位。
4> new申请空间,指针是什么类型,new申请就是什么类型,而malloc申请空间后需要强转才能使用(原因返回是void *)。
5> new、delete区分单个和连续的格式,而malloc、free不区分。
6> new申请对象空间时,会自动调用构造函数,而malloc不会。
7> delete释放对象空间时,会自动调用析构函数,而free不会。
十二、类中特殊的成员函数(重中之重)
1>特殊成员函数的种类:构造函数、析构函数、拷贝构造函数、拷贝赋值函数、移动赋值、移动拷贝、取地址符运算符重载、常取地址符运算符重载
2>特殊原因:
1)这些函数都是系统默认提供,无需程序员手动定义,如果程序员手动定义,则系统取消默认提供。
2)这些函数无需手动调用,在特定的情况下,系统自动调用,即使是程序员手动定义的函数。
1 构造函数
1.1功能
在给类实例化对象时,会自动调用构造函数来给类对象申请空间以及初始化使用。
1.2 格式
函数名:与类同名
返回值:无返回值 也无void
参数:可以无参数,可以有参数
访问权限:一般为public
1.3调用时机
实例化对象时,就会自动调用构造函数
1>栈区
何时实例化对象,何时自动调用构造函数
2>堆区
何时使用new, 何时自动调用构造函数
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 Stu { private : string name; int age; public : Stu () { cout << "无参构造函数" << endl; } Stu (string name, int age) { this ->name = name; this ->age = age; cout << "Stu::有参构造函数" << endl; } void show () { cout << "姓名:" << name << endl; cout << "年纪:" << age << endl; } }; int main () { Stu s1 ("张三" ,19 ) ; Stu s2; 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 #include <iostream> using namespace std; class Stu { private : string name; int age; public : Stu () { cout << "无参构造函数" << endl; } Stu (string name, int age) { this ->name = name; this ->age = age; cout << "Stu::有参构造函数" << endl; } void show () { cout << "姓名:" << name << endl; cout << "年纪:" << age << endl; } }; int main () { Stu s1 ("张三" ,19 ) ; Stu s2; cout << "==============================" << endl; Stu *p = new Stu; Stu *p2 = new Stu ("王二麻" ,99 ); p2->show (); cout << "==============================" << endl; Stu *p3 = (Stu *)malloc (sizeof (Stu)); return 0 ; }
注意:
1> 类中,系统都会提供一个默认无参数的构造函数,如果程序员手动定义构造函数,则系统取消提供的无参构造函数,如果后期想使用无参构造函数,则需要将无参构造函数显性定义出来,否则报错。
2> 可以在构造函数中设置默认参数值。
1.4 初始化列表
构造函数的本身工作是给类对象申请空间的,而初始化工作是由初始化列表来完成。
初始化列表格式:
初始化列表是由构造函数形参小括号后面由冒号引出。
1 2 3 4 类名(形参1 ,形参2 ,形参3 ):成员变量1 (形参1 ),成员变量2 (形参2 ),成员变量3 (形参3 ) { 函数体内容; }
注意:初始化列表只有构造函数才有,其他普通函数没有。
1 2 3 4 5 6 7 8 Stu (string name, int age):name (name),age (age) { cout << "Stu::有参构造函数" << endl; }
必须使用初始化列表的情况:
1>当类中有引用成员时,对该成员的初始化必须使用初始化列表。
2>当类中有常成员变量时,对该成员的初始化必须使用初始化列表来完成。
3>当类中嵌套另一个类对象时,对该对象的初始化,必须使用初始化列表来完成。
结论:
当类中嵌套另一对象时,调用构造函数的顺序:
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 #include <iostream> using namespace std; class Birthday { private : int year; int month; int day; public : Birthday () { cout << "Birthday::无参构造函数" << endl;} Birthday (int y, int m, int d):year (y),month (m),day (d) { cout << "Birthday::有参构造函数" << endl; } void show () { cout << "year= " << year << endl; } }; class Stu { private : string name; int id; Birthday bir; public : Stu () {cout << "Stu::无参构造函数" << endl;} Stu (string name, int id, int year, int month, int day):name (name),id (id),bir (year,month,day) { cout << "Stu::有参构造函数" << endl; } void show () { cout << "name = " << name << endl; cout << "id = " << id << endl; } }; int main () { Stu s1 ("张三" ,1001 ,2002 ,2 ,8 ) ; Stu s2; return 0 ; }
2 析构函数
2.1 功能
当类对象生命周期结束后,会自动调用析构函数,来给类对象回收资源(释放空间)。
2.2 格式
函数名:~类名
返回值:无返回值,也无void
参数:无参数
访问权限:一般为public
2.3 调用时机
当类对象生命周期结束后,会自动调用析构函数。
1>栈区
当类对象所在的函数结束时,会自动调用析构函数释放空间。
2>堆区
何时使用delete,何时自动调用析构函数。
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 #include <iostream> using namespace std; class Stu { private : string name; int id; public : Stu () {cout << "Stu::无参构造函数" << endl;} Stu (string name, int id):name (name),id (id) { cout << "Stu::有参构造函数" << endl; } ~Stu () { cout << "Stu::析构函数" << endl; cout << this << endl; } void show () { cout << "name = " << name << endl; } }; int main () { Stu s1; Stu s2 ("张三" ,1001 ) ; cout << "&1= " << &s1 << " &s2= " << &s2 << endl; return 0 ; }
小结:
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 class Stu { private : string name; int id; double *score; public : Stu () {cout << "Stu::无参构造函数" << endl;} Stu (string name, int id, double s):name (name),id (id),score (new double (s)) { cout << "Stu::有参构造函数" << endl; } ~Stu () { cout << "Stu::析构函数" << endl; delete score; } void show () { cout << "name = " << name << endl; } };
3 拷贝构造函数
3.1 功能
拷贝构造函数是一种特殊的构造函数,使用一个类对象给另一个类对象进行初始化使用的。
3.2 格式
函数名:与类同名
返回值:无返回值,也无void
参数:同类的其他类对象
访问权限:一般为public
1 2 3 4 类名(const 类名 &other) { }
3.3 调用时机
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 #include <iostream> using namespace std; class Stu { private : string name; int id; public : Stu () {cout << "Stu::无参构造函数" << endl;} Stu (string name, int id):name (name),id (id) { cout << "Stu::有参构造函数" << endl; } Stu (const Stu &other):name (other.name),id (other.id) { cout << "Stu::拷贝构造函数" << endl; } ~Stu () { cout << "Stu::析构函数" << endl; } void show () { cout << "name = " << name << endl; cout << "id = " << id << endl; } }; int main () { Stu s1; Stu s2 ("张三" ,1001 ) ; s2.show (); Stu s3 = s2; s3.show (); return 0 ; }
4 浅拷贝和深拷贝(笔试面试题)
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <iostream> using namespace std; class Stu { private : string name; int id; double *score; public : Stu () { cout <<"Stu::无参构造函数" << endl; } Stu (string name, int id, double score):name (name),id (id),score (new double (score)) { cout <<"Stu::有参构造函数" << endl; } Stu (const Stu &other):name (other.name),id (other.id),score (new double (*(other.score))) { cout <<"Stu::拷贝构造函数" << endl; } ~Stu () { cout <<"Stu::析构构造函数" << endl; delete score; } void show () { cout << "name = " << name << endl; } }; int main () { Stu s1; Stu s2 ("zhangsan" ,1001 ,99 ) ; Stu s3 (s2) ; return 0 ; }
5 拷贝赋值函数
5.1 功能
用一个类对象给另一类对象进行赋值操作
本质:运算符重载
5.2 格式
函数名:operator=
返回值:自身的引用
参数:同类的类对象
访问权限:一般为public
1 2 3 4 类名 &operator =(const 类名&other) { 函数体内容; }
5.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 63 64 65 66 67 68 69 70 71 72 73 74 #include <iostream> using namespace std; class Stu { private : string name; int age; public : Stu () { cout << "Stu::无参构造函数" << endl; } Stu (string name, int age):name (name),age (age) { cout << "Stu::有参构造函数" << endl; } Stu (const Stu &other):name (other.name),age (other.age) { cout << "Stu::拷贝构造函数" << endl; } Stu &operator =(const Stu &other) { if (this != &other) { name = other.name; age = other.age; } cout << "Stu:拷贝赋值函数" << endl; return *this ; } ~Stu () { cout << "Stu::析构构函数" << endl; } void show () { cout << name << " " << age << endl; } }; int main () { Stu s1; Stu s2 ("张三" ,18 ) ; s2.show (); Stu s3 = s2; s3.show (); s1 = s3; s1.show (); return 0 ; }
1> 每个类中系统都会提供一个默认的拷贝赋值函数,功能是将一个类对象的所有数据成员赋值给另一个类对象的所有数据成员,如果程序员显性定义出拷贝赋值函数,那么系统取消默认提供。
2>拷贝赋值函数也存在深浅拷贝问题,系统提供的可以称为浅拷贝,深拷贝将拷贝赋值函数(拷贝构造函数)显性定义出来,将有指针成员,自己申请一片空间,再将另一个对象指针指向空间里的值,复制到刚刚申请的空间中去。
十三、匿名对象
1 概念:
没有名字的对象
2 格式:
类名();
3 作用
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 #include <iostream> using namespace std; class Dog { private : string name; string color; int age; public : Dog () {} Dog (string n, string c, int a):name (n),color (c),age (a) {} Dog (const Dog &other):name (other.name),color (other.color),age (other.age) {} void show () { cout << name << " " << color << endl; } }; void fun (Dog d) { } int main () { Dog d1 = Dog ("大黄" ,"pink" ,4 ); d1.show (); Dog d[3 ] = {Dog ("小黑" ,"black" ,7 ),Dog ("小" ,"black" ,7 )}; fun (Dog ("小黑" ,"black" ,7 )); return 0 ; }
十四、友元
1 作用
可以让一些函数或者一些类去访问另一个类的私有数据成员。
2 种类
全局函数做友元
类做友元
成员函数做友元
3 关键字
friend
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 #include <iostream> using namespace std; class Room { friend void goodGay (Room r) ; public : string living_room; private : string bed_room; public : Room () { living_room = "客厅" ; bed_room = "卧室" ; } }; void goodGay (Room r) { cout << "全局函数好基友正在访问。。" << r.living_room << endl; cout << "全局函数好基友正在访问。。" << r.bed_room << endl; } int main () { Room r; goodGay (r); return 0 ; }
5 类作友元
让一个类去访问另一个类的私有属性。
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 Room ; class GoodGay { private : Room *r; public : GoodGay (); void vist () ; }; class Room { friend class GoodGay ; public : string livingRoom; private : string bedRoom; public : Room () { livingRoom = "客厅" ; bedRoom = "卧室" ; } }; GoodGay::GoodGay () { r = new Room; } void GoodGay::vist () { cout << "好基友的类正在访问。。" << r->livingRoom << endl; cout << "好基友的类正在访问。。" << r->bedRoom << endl; } int main () { GoodGay g; g.vist (); return 0 ; }
6 成员函数做友元(了解)
让一个类中的某个成员函数去访问另一个类的私有属性。
总结:
1> 不要过度的使用友元,会降低封装性,破坏封装性。
2> 友元不具有交互性、传递性、继承性。
十五、常成员函数和常对象(const)
类中所有的成员函数都可以对数据成员做修改操作,如果想让一个成员函数不能对数据成员最修改操作,此时就需要用常成员函数完成。
课前热身
1 2 3 4 5 6 7 8 9 10 int *p;int * const p; int const *p; int const * const p; const int fun () { }
1 常成员函数
常成员函数不能对数据成员做修改操作。
格式:
1 2 3 4 返回值类型 函数名(形参列表) const { 函数体内容; }
注意:
一个类中,常成员函数与普通成员函数同名,不是重复定义,而是重载关系,主要原因: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 #include <iostream> using namespace std; class Stu { private : string name; int age; public : Stu () {} Stu (string name,int age):name (name),age (age) {} void show () { this ->age = 48 ; cout << age << endl; } void show () const { cout << this ->age << endl; } }; int main () { Stu s1 ("张三" ,18 ) ; s1.show (); return 0 ; }
2 常对象
常对象表示该对象的数据成员不能被改变。
格式:
const 类名 对象名;
总结
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 #include <iostream> using namespace std; class Stu { private : string name; int age; public : Stu () {} Stu (string name,int age):name (name),age (age) {} void init (string n, int a) { name = n; age = a; } void show () const { cout << this ->age << endl; } }; int main () { const Stu s ("zhang" ,78 ) ; const Stu s1 ("张三" ,18 ) ; s1.show (); return 0 ; }
3 mutable关键字
mutable修饰成员变量,表示该成员变量可以在常成员函数中被修改(取消常属性)。
十五、运算符重载
1 概念
运算符重载就是对运算符进行重新定义,赋予另一种功能,以适应不同的数据类型。
每个运算符重载都有两种实现方式:
1> 成员函数实现
2> 全局函数实现
2 算术运算符重载
种类:+ 、 - 、 * 、 /、 %
表达式: L # R (L 左操作数 # 运算符 R右操作数)
左操作数:可以是左值,也可以是右值,运算过程中不能被改变。
右操作数:可以是左值,也可以是右值,运算过程中不能被改变。
结果:右值(不可以被改变)
1> 成员函数实现算术运算符重载:
1 2 3 4 5 6 7 8 9 const 类名 operator #(const 类名 &R) const {}
2> 全局函数实现算术运算符重载:
1 2 3 const 类名 operator #(const 类名 &L, const 类名 &R){}
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; class Person { private : int a; int b; public : Person () {} Person (int a, int b):a (a),b (b) {} const Person operator +(const Person &R) const { Person temp; temp.a = a + R.a; temp.b = b + R.b; return temp; } void show () { cout << "a = " << a << " b = " << b << endl; } }; int main () { Person p1 (23 ,56 ) ; Person p2 (12 ,10 ) ; Person p3 = p1 + p2; p3.show (); return 0 ; }
3 关系运算符重载
种类: > 、 >= 、 < 、<= 、== 、 !=
表达式:L # R (L 左操作数 # 运算符 R右操作数)
左操作数:可以是左值,也可以是右值,运算过程中不能被改变。
右操作数:可以是左值,也可以是右值,运算过程中不能被改变。
结果:bool
1> 成员函数实现关系运算符重载:
1 2 3 bool operator #(const 类名 &R) const {}
2> 全局函数实现关系运算符重载:
1 2 3 bool operator #(const 类名 &L, const 类名 &R){}
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 #include <iostream> using namespace std; class Person { private : int a; int b; public : friend bool operator >(const Person &L, const Person &R); Person () {} Person (int a, int b):a (a),b (b) {} const Person operator +(const Person &R) const { Person temp; temp.a = a + R.a; temp.b = b + R.b; return temp; } void show () { cout << "a = " << a << " b = " << b << endl; } }; bool operator >(const Person &L, const Person &R){ if (L.a > R.a && L.b > R.b) { return true ; } else { return false ; } } int main () { Person p1 (23 ,56 ) ; Person p2 (12 ,10 ) ; Person p3 = p1 + p2; p3.show (); if (p1 > p2) { cout << "p1 > p2" << endl; } return 0 ; }
4 赋值运算符重载
种类:= 、 += 、 -= 、*= 、/= 、%=
表达式: L # R (L左操作数 #运算符 R右操作数)
左操作数:只能是左值,运算过程中要发生变化。
右操作数:可以是左值,也可以是右值,运算过程中,不能发生变化。
结果:自身的引用
1> 成员函数实现赋值运算符重载:
1 2 3 类名 & operator #(const 类名 &R) {}
2> 全局函数实现赋值运算符重载:
1 2 3 类名& operator #(类名 &L, const 类名 &R) {}
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 #include <iostream> using namespace std; class Person { private : int a; int b; public : friend bool operator >(const Person &L, const Person &R); friend Person &operator +=(Person &L, const Person &R); Person () {} Person (int a, int b):a (a),b (b) {} const Person operator +(const Person &R) const { Person temp; temp.a = a + R.a; temp.b = b + R.b; return temp; } void show () { cout << "a = " << a << " b = " << b << endl; } }; bool operator >(const Person &L, const Person &R){ if (L.a > R.a && L.b > R.b) { return true ; } else { return false ; } } Person &operator +=(Person &L, const Person &R) { L.a += R.a; L.b += R.b; return L; } int main () { Person p1 (23 ,56 ) ; Person p2 (12 ,10 ) ; Person p3 = p1 + p2; p3.show (); if (p1 > p2) { cout << "p1 > p2" << endl; } p1+=p2; p1.show (); return 0 ; }
5 自增、自减运算符重载
种类:++ 、 –
以自增为例:
1) 前置自增
表达式:++O (O操作数)
操作数:只能是左值,运算过程中需要被改变
结果:左值 (自身的引用)
1>成员函数实现前置自增运算符重载:
2>全局函数实现前置自增运算符重载:
1 2 3 类名 &operator ++(类名 &O) {}
2) 后置自增
表达式:O++
操作数:只能是左值,运算过程中需要被改变
结果:右值 (不能被改变)
1>成员函数实现后置自增运算符重载:
1 2 3 const 类名operator ++(int ){}
2>全局函数实现后置自增运算符重载:
1 2 3 const 类名operator ++(类名 &O,int ){}
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 #include <iostream> using namespace std; class Person { private : int a; int b; public : friend bool operator >(const Person &L, const Person &R); friend Person &operator +=(Person &L, const Person &R); friend Person &operator ++(Person &O); friend const Person operator ++(Person &O, int ); Person () {} Person (int a, int b):a (a),b (b) {} const Person operator +(const Person &R) const { Person temp; temp.a = a + R.a; temp.b = b + R.b; return temp; } void show () { cout << "a = " << a << " b = " << b << endl; } }; bool operator >(const Person &L, const Person &R){ if (L.a > R.a && L.b > R.b) { return true ; } else { return false ; } } Person &operator +=(Person &L, const Person &R) { L.a += R.a; L.b += R.b; return L; } Person &operator ++(Person &O) { ++O.a; ++O.b; return O; } const Person operator ++(Person &O, int ){ Person temp; temp.a = O.a++; temp.b = O.b++; return temp; } int main () { Person p1 (23 ,56 ) ; Person p2 (12 ,10 ) ; Person p3 = p1 + p2; p3.show (); if (p1 > p2) { cout << "p1 > p2" << endl; } p1+=p2; p1.show (); cout << "=====================" << endl; Person p4 (10 ,10 ) ; p1 = ++p4; p4.show (); p1.show (); cout << "=====================" << endl; Person p5 (20 ,20 ) ; p2 = p5++; p2.show (); p5.show (); return 0 ; }
6 插入提取运算符重载
插入符 <<
提取符 >>
cout 是ostream类的类对象
cin 是istream类的类对象
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 #include <iostream> using namespace std; class Person { private : int a; int b; public : friend bool operator >(const Person &L, const Person &R); friend Person &operator +=(Person &L, const Person &R); friend Person &operator ++(Person &O); friend const Person operator ++(Person &O, int ); friend ostream &operator <<(ostream &cout, const Person &p); friend istream & operator >>(istream &cin, Person &p); Person () {} Person (int a, int b):a (a),b (b) {} const Person operator +(const Person &R) const { Person temp; temp.a = a + R.a; temp.b = b + R.b; return temp; } void show () { cout << "a = " << a << " b = " << b << endl; } }; bool operator >(const Person &L, const Person &R){ if (L.a > R.a && L.b > R.b) { return true ; } else { return false ; } } Person &operator +=(Person &L, const Person &R) { L.a += R.a; L.b += R.b; return L; } Person &operator ++(Person &O) { ++O.a; ++O.b; return O; } const Person operator ++(Person &O, int ){ Person temp; temp.a = O.a++; temp.b = O.b++; return temp; } ostream &operator <<(ostream &cout, const Person &p) { cout << p.a << endl; cout << p.b << endl; return cout; } istream & operator >>(istream &cin, Person &p) { cin >> p.a >> p.b ; return cin; } int main () { Person p1 (23 ,56 ) ; Person p2 (12 ,10 ) ; Person p3 = p1 + p2; p3.show (); if (p1 > p2) { cout << "p1 > p2" << endl; } p1+=p2; p1.show (); cout << "=====================" << endl; Person p4 (10 ,10 ) ; p1 = ++p4; p4.show (); p1.show (); cout << "=====================" << endl; Person p5 (20 ,20 ) ; p2 = p5++; p2.show (); p5.show (); cout << "=====================" << endl; cout << p2; Person p; cin >> p; cout << p; return 0 ; }
7 不能重载的运算符
对象访问成员 .
指针访问成员 ->
三目运算符 ? :
sizeof()
作用域限定符 ::
十六、静态成员
静态数据成员和静态成员函数是属于类的,不属于类的某个实例,它们在类的所有实例中都是共享的。
在数据成员前加 static ------>静态数据成员
在成员函数前加 static ------>静态成员函数
静态数据成员必须在类外初始化,如果不初始化(不建议),默认为0。
静态成员函数只能访问静态数据成员,不能访问非静态数据成员。
格式:
1 2 3 4 5 6 7 8 9 10 11 class 类名{ static 数据类型 变量名; 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 #include <iostream> using namespace std; class BankAccount { private : double balance; static double interestRate; public : BankAccount (double m):balance (m) {} static double getInterestRate () { return interestRate; } static double getLastMoney (const BankAccount &account) { return account.balance*interestRate+account.balance; } static void update (double rate) { interestRate = rate; } void show () {} }; double BankAccount::interestRate = 0.05 ; int main () { cout << BankAccount::getInterestRate () << endl; BankAccount::update (0.03 ); cout << BankAccount::getInterestRate () << endl; BankAccount zhangsan (1000 ) ; cout << "连本带利的余额: " << BankAccount::getLastMoney (zhangsan) << endl; return 0 ; }
十七、继承
类的三大属性:封装、继承、多态
1 目的
1> 实现代码的复用性
2> 建立多个类(父类与子类)之间的联系
3> 通过继承,实现多态的特点
2 继承相关概念
保持已有类的特性,在原来的基础上增加新的特性而构造出新类的过程,称为继承/派生。
被继承者称为 父类 / 基类
继承者称为 子类 / 派生类
3 格式
1 2 3 4 5 6 7 class 类名:继承方式 类名{ 子类的拓展; };
4 继承方式
1 2 3 4 5 6 7 8 父类中成员的访问权限 public | protected | private public | protected | private public | protected | private 继承方式 public protected private 子类通过该继承方式,继承到子类中 public | protected | 不可访问 protected | protected | 不可访问 private | 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 69 70 71 72 73 74 75 #include <iostream> using namespace std; class Person { private : string name; protected : int age; public : int h; Person () {} Person (string name, int age, int h):name (name),age (age),h (h) { cout << "父类的有参构造函数" << endl; } void show () { cout << name << endl; cout << age << endl; } }; class Stu :public Person{ private : double score; public : Stu () {} Stu (string name, int age, int h, double score):Person (name,age,h),score (score) { cout << "子类的有参构造函数" << endl; } void show () { cout << score << endl; cout << h << endl; cout << age << endl; } }; int main () { Stu s1 ("张三" ,18 ,185 ,88 ) ; s1.show (); cout << "====================" << endl; s1.Person::show (); return 0 ; }
5 继承中的特殊成员函数
5.1 构造函数
基类中的构造函数会被继承到派生类中,先调用基类的构造函数来给派生类从基类中继承下来的数据成员初始化。(先构造父类,再构造子类)
5.2 析构函数
父类中的析构函数也会被继承到子类中,子类析构后,再调用父类的析构函数进行释放空间。(先构造的,后析构 后构造的,先析构)
5.3 拷贝构造函数
父类中的拷贝构造函数会被继承到子类中,调用父类中的拷贝构造函数来完成对子类从父类继承下来的数据成员初始化工作。
如果涉及到深拷贝,则需要在父类、子类中各自完成深拷贝工作。
5.4 拷贝赋值函数
父类中的拷贝赋值函数会被继承到子类中,调用父类中的拷贝赋值函数来完成对子类从父类继承下来的数据成员赋值工作。
如果涉及到深拷贝,则需要在父类、子类中各自完成深拷贝工作。
小结:
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 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 139 140 141 142 143 144 145 #include <iostream> using namespace std; class Person { private : string name; protected : int age; public : int h; Person () {} Person (string name, int age, int h):name (name),age (age),h (h) { cout << "父类的有参构造函数" << endl; } Person (const Person &other):name (other.name),age (other.age),h (other.h) { cout << "Person::拷贝构造函数" << endl; } Person &operator =(const Person &other) { if (this != &other) { name = other.name; age = other.age; h = other.h; } cout << "Person::拷贝赋值函数" << endl; return *this ; } ~Person () { cout << "Person::析构函数" << endl; } void show () { cout << name << endl; cout << age << endl; } }; class Stu :public Person{ private : double score; public : Stu () {} Stu (string name, int age, int h, double score):Person (name,age,h),score (score) { cout << "子类的有参构造函数" << endl; } Stu (const Stu &other):Person (other), score (other.score) { cout << "Stu::拷贝构造函数" << endl; } Stu &operator =(const Stu &other) { if (this != &other) { score = other.score; Person::operator =(other); } cout << "Stu::拷贝赋值函数" << endl; return *this ; } ~Stu () { cout << "Stu::析构函数" << endl; } void show () { cout << score << endl; cout << h << endl; cout << age << endl; } }; int main () { Stu s1 ("张三" ,18 ,185 ,88 ) ; s1.show (); cout << "====================" << endl; s1.Person::show (); cout << "====================" << endl; Stu s2 = s1; cout << "====================" << endl; Stu s3; s3 = s1; return 0 ; }
十八、多继承
1 概念
一个类由多个类共同派生。
2 格式
1 2 3 4 class 类名:继承方式1 类名1 ,继承方式2 类名2 ,继承方式3 类名3 ,...,继承方式n 类名n{ 子类的拓展; };
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <iostream> using namespace std; class Sofa { private : string sitting; public : Sofa () {} Sofa (string s):sitting (s) { cout << "Sofa::有参构造函数" << endl; } void show () { cout << sitting << endl; } }; class Bed { private : string sleep; public : Bed () {} Bed (string s):sleep (s) { cout << "Bed::有参构造函数" << endl; } void show () { cout << sleep << endl; } }; class SofaBed :public Sofa,public Bed{ private : string color; public : SofaBed () {} SofaBed (string sit,string sleep, string c):Sofa (sit),Bed (sleep),color (c) { cout << "SofaBed::有参构造函数" << endl; } void show () { cout << color << endl; } }; int main () { SofaBed s ("可坐" ,"可躺" ,"pink" ) ; s.Sofa::show (); s.Bed::show (); return 0 ; }
十九、菱形继承
1 概念
菱形继承又称为钻石继承,是由公共基类派生出多个中间子类,又由多个中间子类共同派生出汇聚子类。汇聚子类会得到多份中间子类从公共基类继承下来的数据成员,会造成空间浪费,没有必要。
问题:
1> 汇聚子类会得到多份中间子类从公共基类继承下来的数据成员,会造成空间浪费,没有必要。
2>会对公共基类的数据成员进行多次初始化,或多次释放。
解决问题:虚继承
2 格式
1 2 3 4 5 6 A --------> 公共基类 / \ B C --------> 中间子类 \ / D --------> 汇聚子类
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 JiaJU { private : int w; public : JiaJU () {} JiaJU (int w):w (w) { cout << "JiaJu::有参构造函数" << endl; } ~JiaJU () { cout << "JiaJu::析构函数" << endl; } }; class Sofa :public JiaJU{ private : string sitting; public : Sofa () {} Sofa (string s,int w):JiaJU (w),sitting (s) { cout << "Sofa::有参构造函数" << endl; } ~Sofa () { cout << "Sofa::析构函数" << endl; } void show () { cout << sitting << endl; } }; class Bed :public JiaJU{ private : string sleep; public : Bed () {} Bed (string s,int w):JiaJU (w),sleep (s) { cout << "Bed::有参构造函数" << endl; } ~Bed () { cout << "Bed::析构函数" << endl; } void show () { cout << sleep << endl; } }; class SofaBed :public Sofa,public Bed{ private : string color; public : SofaBed () {} SofaBed (string sit,string sleep, string c,int w):Sofa (sit,w),Bed (sleep,w),color (c) { cout << "SofaBed::有参构造函数" << endl; } ~SofaBed () { cout << "SofaBed::析构函数" << endl; } void show () { cout << color << endl; } }; int main () { SofaBed s ("可坐" ,"可躺" ,"pink" ,200 ) ; return 0 ; }
二十、虚继承
1 作用
解决菱形继承存在的问题。
2 格式
在中间子类继承方式前加 virtual
1 2 3 4 class 类名:virtual 继承方式 类名{ 中间子类的拓展; };
3 小结
虚继承会解决菱形继承存在的问题(具体阐述什么问题),由于只需要保留一份中间子类从公共基类继承下来的数据成员,程序不知该保留哪个中间子类的,所以就会直接调用公共基类的无参构造,导致公共基类的数据成员无法初始化,所以需要在汇聚子类中手动调用公共基类的有参构造函数,来完成公共基类的数据成员初始化操作。
二一、多态
类的三大属性:封装、继承、多态
静态多态 (编译时)-----> 函数重载
动态多态 (运行时)
多态:一种形式多种状态,就比如一个人有很多角色,有着不同的行为操作,取决于不同的情景。
1 概念
多态:父类的指针或者引用,可以指向或者初始化子类的对象,调用子类对父类重写的函数,进而展开子类的功能。
2 函数重写
1> 必须有继承关系
2> 子类和父类有同名同类型的函数
3> 父类中的该函数必须是虚函数
3 虚函数
1> 在函数前加 virtual ----> 虚函数
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <iostream> using namespace std; class Zhou { private : string name; int age; public : Zhou (){} Zhou (string name, int age):name (name),age (age) {} virtual void speak () { cout << "阿巴阿巴阿巴。。" << endl; } }; class Teacher :public Zhou{ private : int id; public : Teacher (){} Teacher (string name, int age, int id):Zhou (name,age),id (id) {} void speak () { cout << "同学们,今天学习多态,认真听讲。。" << endl; } }; class Player :public Zhou{ private : string name; public : Player (){} Player (string name, int age, string n):Zhou (name,age),name (n) {} void speak () { cout << "卧************ 一波一波。。" << endl; } }; int main () { Teacher t ("周老师" ,18 ,1001 ) ; Player g ("又菜又爱玩" ,3 ,"刺激战场" ) ; Zhou *p; p = &t; p->speak (); p = &g; p->speak (); return 0 ; }
4 赋值兼容规则
父类的指针或者引用,可以指向或者初始化子类的对象
5 多态中,实现函数重写的原理
1> 类中有虚函数时,虚函数就会有一个虚指针
2> 虚指针在类的最前面,指向了虚函数表,虚函数表里记录了虚函数
3> 虚指针和虚函数表是实现多态的重要机制
6 虚析构函数
因为父类的指针指向子类对象,只作用于子类从父类继承下来的那片空间,如果释放父类指针,那么只会释放子类从父类继承下来的那片空间,而子类自己拓展的空间,并没有得到释放,造成内存泄漏。
作用:
1> 虚析构函数可以正确引导子类调用析构函数释放自己拓展空间
2> 在父类的析构函数前加virtual ,虚析构函数也满足继承。
实例:
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 Person { private : string name; public : Person (){} Person (string name):name (name) { } virtual ~Person () { cout <<"Person::析构函数" << endl; } }; class Stu :public Person{ private : int age; public : Stu (){} Stu (string name, int age):Person (name),age (age) {} ~Stu () { cout << "Stu::析构函数" << endl; } }; int main () { Person *p = new Stu ("张三" ,18 ); delete p; return 0 ; }
7 纯虚函数
当父类中的虚函数,只是用来被子类进行重写的,并且没有实现的意义,此时就可以将该虚函数设置成纯虚函数。
格式:
1 2 3 virtual 返回值类型 函数名(形参列表) = 0 ;
8 抽象类
概念:
抽象类不能具体实例化一个对象, 一般用来被继承的。
如果一个类中至少有一个纯虚函数,那么该类就是抽象类。
如果父类是抽象类,子类继承父类,但是没有对父类中的纯虚函数进行重写,那么子类也是抽象类,就不能实例化对象。
实例:
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; class A { private : int a; public : virtual void show () = 0 ; }; class B :public A{ public : void show () {}}; int main () { B b; return 0 ; }
试编程:
封装一个抽象类(动物类),再封装两个类,分别是狗,猫这样的类,通过子类对父类的中void speak(纯虚函数)重写,实现,父类指针调用不同子类的speak函数,实现不同的结果。
二二、模板
1> 模板就是建立一个通用模具,大大提高代码的复用性。
2> C++另一种编程思想: 泛型编程 主要利用技术 模板
3> C++提供了两个重要的模板机制: 函数模板 和 类模板
生活中的例子:
1 模板的特点
1> 模板是通用的,不是万能的
2> 模板不可以直接使用
2 函数模板
函数模板就是建立一个通用的函数,其函数的参数类型不具体制定,用一个虚拟类型来代替。
2.1格式
1 2 template <typename T> 或 template <class T >函数的定义
解释:
template --------> 表示声明开始创建模板
typename --------> 表示其后面的是一种数据类型 (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 #include <iostream> using namespace std; template <typename T,typename N>T add (T x, N y) { return x+y; } int main () { cout << add (1 ,2 ) << endl; cout << add (2.3 ,5.6 ) << endl; cout << add ('1' ,'2' ) << endl; add ("---" ,9 ); return 0 ; }
3 类模板
类模板就是建立一个通用的类,其类中的数据成员类型不具体制定,用一个虚拟类型来代替。
3.1格式
1 2 template <typename T>类的声明
解释:
template --------> 表示声明开始创建模板
typename --------> 表示其后面的是一种数据类型 (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 #include <iostream> using namespace std; template <typename T,typename N>class B { private : T name; N age; public : B (){} B (T name, N age):name (name),age (age) {} }; int main () { B<string, int > b1 ("张三" ,18 ) ; B<int , string> b2 (100 ,"男" ) ; return 0 ; }
二三、auto
1 概念
C++11引入了自动类型推导,和Python不一样,C++中需要auto关键字引导。
2 作用
auto修饰变量时,可以自动推导出变量的数据类型
3 注意
1> auto修饰变量时,必须初始化
2> auto的右值,可以是右值,可以是表达式,也可以是函数的返回值
3> auto不能修饰函数的形参
4> auto不能修饰数组
5> auto不能修饰非静态数据成员
4 用途
1> auto 一般修饰数据类型表冗长的类型。
2> auto还可以修饰依赖模板参数的类型
3> auto可以修饰lambda表达式。
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; int fun (int a, char b, double c, string d, int *e, char *f, double *g) { return 10 ; } template <typename T>void fun2 (T a) { auto b = a; cout << typeid (b).name () << endl; } int main () { int (*p)(int , char , double , string , int *, char *, double *) = fun; auto p2 = fun; fun2 (1 ); fun2 ('f' ); return 0 ; }
二四、lambda表达式 (C++11)
1 作用
当需要一个匿名的,临时的,可以捕获外界变量的函数 时,可以用lambda表达式 完成。
2 格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [](){} [捕获外界变量的方式](形参列表)->return type{函数体内容} []:捕获外界变量的方式 1. [变量1 ,变量2 ] :表示进行值捕获,捕获的到变量和外界变量不是同一个变量,但是值相同,且不能 进行修改操作,如果想修改,则需要加上mutable 关键字 2. [=] : 表示对外界的所有变量进行值捕获,捕获的到变量和外界变量不是同一个变量,但是值相同,且不能 进行修改操作,如果想修改,则需要加上mutable 关键字 3. [&变量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 #include <iostream> using namespace std; int main () { int a = 100 , b = 200 ; cout << "main: a = " << a << " &a = " << &a << endl; auto fun = [=]()mutable { cout << "a = " << a << " &a = " << &a << endl; a = 200 ; cout << "a = " << a << " &a = " << &a << endl; cout << b << endl; }; fun (); cout << "main: a = " << a << " &a = " << &a << endl; return 0 ; }
案例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 #include <iostream> using namespace std; int main () { int a = 100 , b = 200 ; cout << "main: a = " << a << " &a = " << &a << endl; auto fun = [&](){ cout << "a = " << a << " &a = " << &a << endl; a = 200 ; cout << "a = " << a << " &a = " << &a << endl; cout << b << endl; }; fun (); cout << "main: a = " << a << " &a = " << &a << endl; return 0 ; }
二五、C++中的数据类型转换
1 概念
C++中存在多种数据类型的转换方式,用于在不同的数据类型之间进行转换。
2 种类
以下是常见的数据类型转换方式:
1>隐式类型转换(自动类型转换)
这是C++编译器自动执行的类型转换,通常在表达式中出现时发生。例如,将较小的整数转换为较大的整数类型,将整数提升为浮点数等。
1 2 3 int num_int = 10; double num_double = num_int; // 隐式将int转换为double
2>显示类型转换(强制类型转换)
通过使用强制类型转换操作符 来显示执行类型转换。这种转换可能会导致数据的截断或者精度丢失 ,因此要小心使用。
(1)静态转换(static_cast)
用于基本数据类型之间的转换
以及父类指针/引用转换为子类指针/引用
还可以用于不同类型的指针之间的转换
1 2 double num_double = 3.14 ; int num_int = static_cast <int >(num_double);
(2)动态转换(dynamic_cast)
通常用于多态类之间的指针或引用类型转换,确保类型安全。在运行时进行类型检查,只能用于具有虚函数的类之间的转换
1 2 3 4 5 6 7 class Base { virtual void foo () {} }; class Derived : public Base {}; Base* base_ptr = new Derived; Derived* derived_ptr = dynamic_cast <Derived*>(base_ptr);
(3)常量转换(const_cast)
用于添加或移除指针或引用的常量性。它可以用来去除const限定符,但要注意潜在的未定义行为
1 2 3 const int a =10 ; int *p;p = &a;
1 2 const int num_const = 5 ; int * num_ptr = const_cast <int *>(&num_const);
(4)重新解释转换(reinterpret_cast)
执行低级别的位模式转换,通常用于指针之间的类型转换。它可能导致未定义行为,因此要谨慎使用
1 2 int num = 42 ; float * float_ptr = reinterpret_cast <float *>(&num);
3> C风格类型转换
与C语言中的类型转换方式类似,包括以下几种:
c样式转换:使用强制类型转换 操作符进行转换,类似与C语言中的类型转换
1 2 int num_int = 10 ; double num_double = (double )num_int;
函数样式转换(函数式转换):使用C++中的类型转换函数进行转换
1 2 int num_int = 10 ; double num_double = double (num_int);
3 注意
需要注意的是,尽管C++提供了多种类型转换方式,但应该谨慎使用,以避免潜在的错误和问题。特别是在使用强制类型转换时,务必确保转换操作是安全的,以避免不必要的问题。
关键字总结
1> C++中一共有63个关键字,如上图所示,其中标红的为c语言中的关键字,有32个
asm
:这是一个用于嵌入汇编语言代码的关键字。它允许你在C++代码中直接插入汇编指令,通常用于执行特定的底层操作。然而,由于现代C++提供了更强大的抽象和跨平台性,通常不建议使用这个关键字。
explicit
:这个关键字通常用于禁止隐式类型转换的发生。当一个构造函数被声明为explicit 时,它将不会在隐式类型转换中被调用,只能在显式构造函数调用中使用。
export
:在C++中,export 关键字用于指示一个模板的定义将在另一个文件中实例化。然而,在实际的C++标准中,export 关键字的语法并未最终确认,并且在许多编译器中也未被实现。在C++20中,export 被重新引入,但是它的主要用途是与模块化编程相关,而不是之前模板实例化的用法。
goto
:goto 是一个跳转语句,允许你无条件地将程序的控制转移到指定的标签处。然而,由于使用goto 会导致代码结构变得混乱和难以维护,现代编程实践通常建议避免使用它。
register
:在早期的C语言标准中,register 关键字用于建议编译器将变量存储在寄存器中,以便提高访问速度。然而,现代编译器已经能够智能地管理寄存器分配,所以使用register 关键字通常不再有明显的性能提升,并且在C++17中已被弃用。
volatile
:volatile 关键字用于告诉编译器不要对标记为volatile 的变量进行优化,因为这些变量的值可能会在未知的时间被外部因素改变,比如硬件中断或多线程环境中的共享变量。这可以防止编译器对这些变量的读取和写入操作进行优化,以确保程序的行为是可预测的。
2> 数据类型相关的关键字
bool、true、false:对于bool类型数据的相关处理,值为true和false
char、wchar_t:char是单字符数据,wchar_t多字符数据
int、short、float、double、long:整数和实数的数据类型
signed、unsigned:定义有符号和无符号数据的说明符
auto:在c语言中,是存储类型,但是在C++中,是类型自动推导,注意事项有两个:
i> 连续定义多个变量时,初始值必须是相同数据类型,否则报错
ii> auto p=&m; 与auto* p = &m;规定是一样
explicit:防止数据隐式转换
typedef:类型重定义
sizeof:求数据类型的字节运算
3> 语句相关的关键字
switch、case、default:实现多分支选择结构
do、while、for:循环相关的关键字
break、continue、goto:跳转语句
if、else:选择结构
inline:内联函数
return:函数返回值
4> 存储类型相关的关键字
static、const、volatile、register、extern、auto
5> 构造数据类型相关
struct、union:结构体和共用体
enum:枚举
class:类
6> 访问权限:public、protected、private
7> 异常处理:throw、try、catch
8> 类4中相关使用关键字
this:指代自己的指针
friend:友元
virtual:虚
delete、default:对类的特殊成员函数的相关使用
例如:Test(const Test &) = default; ~Test() = delete;
mutable:取消常属性
using:引入数据,有三种使用方式
i> 使用命名空间的关键字
ii> 相当于类型重定义
iii> 修改子类中从父类继承下来成员的权限
operator:运算符重载关键字
9> 类型转换相关的关键字
static_cast、dynamic_cast、const_cast、reinterpret_cast
10> 模板相关的关键字:template、typename
11> 命名空间相关:using、namespace
12> export:导入相关模板类使用
13> 内存分配和回收:new、delete
二六、异常
上的了厅堂,下得了厨房。 写的了代码 , 查的出异常
可以优雅解决代码中的异常
try ------> 将可能发生异常的地方用try进行包裹
throw ------> 将发生异常的地方,使用throw进行抛出异常
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> using namespace std; int fun (int x, int y) { if (y!=0 ) { return x/y; } else { throw 'w' ; } } int main () { try { fun (2 ,0 ); cout << "hello world!" << endl; } catch (char e) { if (e == 'w' ) { cout << "分母不能为0" << endl; } } return 0 ; }
二七、C++标准模板库 (STL)
C++中的标准模板库 (Standard Template Library),STL是标准库之一。
标准模板库中提供大量的函数模板和类模板,用来对数据结构的处理。
STL 的组成:容器、算法、迭代器
容器:置物之所也
数组、链表、二叉树、哈希、、
算法:问题之解法也
增、删、改、查
迭代器:是容器和算法之间的粘合剂 (== 指针)
使用vector需要包含头文件 #include
1 vector 容器
vector就是数组,也称为单端数组,和普通数组有区别,普通数组是静态空间,而vector的空间时动态拓展。
动态拓展,不是在原来空间后面续接新的空间,而是,重新申请空间,将原来空间里的数据,复制到刚刚申请的空间中,再释放原来的空间。
2 vector构造函数
函数原型:
vector< T > v; //无参构造
vector(const vectcor &v) ; //拷贝构造函数
vector(n,elem); //将n个elem拷贝给对象本身
vector(v.begin(),v.end()); //将区间[v.begin(),v.end())拷贝给对象本身
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> #include <vector> using namespace std; void printVector (vector<int > &v) { vector<int >::iterator iter; for (iter = v.begin (); iter != v.end (); iter++) { cout << *iter << " " ; } cout << endl; } int main () { vector<int > v; v.push_back (10 ); v.push_back (20 ); v.push_back (30 ); v.push_back (40 ); v.push_back (50 ); printVector (v); vector<int > v2 = v; printVector (v2); vector<int > v3 (6 ,6 ) ; printVector (v3); vector<int > v4 (v3.begin(), v3.end()) ; printVector (v4); return 0 ; }
3 vector赋值函数
函数原型:
vector &operator=(const vector &v); //拷贝赋值函数
assgin(v.begin(),v.end()); //将区间[v.begin(),v.end())赋值给对象本身
assgin(n,elem); //将n个elem赋值给对象本身
1 2 3 4 5 6 7 8 9 10 11 12 13 vector<int > v5; v5 = v2; printVector (v5); v5.assign (v3.begin (),v3.end ()); printVector (v5); v5.assign (8 ,99 ); printVector (v5);
4 vector的容量大小
empty() ; //判读容器是否为空
size(); //容器的大小,元素的个数
capacity(); // 容量的大小
resize(); //重新设置大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (!v5.empty ()){ cout << v5.size () << endl; cout << v5.capacity () << endl; } v5.resize (10 ); printVector (v5); v3.resize (3 ); printVector (v3);
5 vector的插入和删除
函数原型:
push_back(); //尾插
pop_back(); //尾删
insert(iterator_pos, elem); //在迭代器所指向的位置插入数据
insert(iterator_pos, n,elem);//在迭代器所指向的位置插入n个数据
eraser(v.begin(),v.end()); //将区间[v.begin(),v.end())的元素删除
clear(); //清空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 v3 = v; v3.pop_back (); printVector (v3); v3.insert (v3.begin ()+1 ,99 ); printVector (v3); v3.insert (v3.begin ()+3 ,5 ,100 ); printVector (v3); v3.erase (v3.begin (),v3.end ()); printVector (v3);
6 vector的元素访问
函数原型:
opeator[](int idx);
at(int idx);
front(); //第一个元素
back(); //最后一个元素
二八、list
1概念
功能:
将数据进行链式存储
链表(list) 是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。
链表的组成:
链表由一系列节点组成
节点的组成:
一个是存储数据元素的数据域,另一个是存储下一个节点地址的 指针域
STL中的链表是一个双向链循环链表
list的优点:
list缺点:
链表灵活,但是空间(指针域)和 时间(遍历)额外消耗比较大
2 list构造函数
函数原型:
1 2 3 4 5 6 7 - list<T> lst; - list (beg, end); - list (n, elem); - list (const list<T>& l);
3 list赋值和交换
函数原型:
1 2 3 4 5 6 7 - assign (beg, end); - assign (n, elem); - list& operator =(const list &lst); - swap (lst);
6.4 list大小操作
函数原型:
1 2 3 4 5 6 7 8 9 10 - size (); - empty (); - resize (num); - - resize (num, elem);
6.5 list插入和删除
函数原型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - push_back (); - pop_back (); - push_front (elem); - pop_front (); - insert (const_iterator pos, ele); - insert (const_iterator pos, int count, ele); - insert (pos,beg, end); - erase (const_iterator pos); - erase (const_iterator start, const_iterator end); - clear (); - remove (elem);
6.6 list 数据存取
函数原型:
二九、智能指针
略