黑马程序员C++核心编程学习笔记 一、内存 1.1 内存四区 C++程序在执行时,将内存大致分为4个区域:代码区,全局区,栈区,堆区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int g_a = 10 ;const int c_g_a = 10 ;int main () { int l_a = 10 ; static int s_a = 10 ; const char * a = "hello" ; const int c_l_a = 10 ; cout << "局部变量l_a的十进制地址为:" << (int )&l_a << endl ; cout << "全局变量g_a的十进制地址为:" << (int )&g_a << endl ; cout << "静态变量s_a的十进制地址为:" << (int )&s_a << endl ; cout << "字符串常量a的十进制地址为:" << (int )a << endl ; cout << "const修饰的全局变量c_g_a的十进制地址为:" << (int )&c_g_a << endl ; cout << "const修饰的局部变量c_l_a的十进制地址为:" << (int )&c_l_a << endl ; return 0 ; } 12345678910111213141516171819202122
1 2 3 4 5 6 7 8 9 10 11 12 13 int * funcation () { int a = 10 ; return &a; } int main () { int * p = funcation(); cout <<"局部变量a的值为:" << *p << endl ; cout << "局部变量a的值为:" << *p << endl ; return 0 ; } 123456789101112
堆区 :由程序员分配(new
)和释放(delete
),若程序员不释放,程序结束时由操作系统回收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int * funcation () { int * p = new int (10 ); return p; } int main () { int * p = funcation(); cout << *p << endl ; cout << *p << endl ; return 0 ; } 1234567891011121314
Tip:
程序运行前分为:代码区,全局区
程序运行后分为:栈区,堆区
[注]: 关于内存更详细的知识👉深度剖析数据在内存中的存储 👉几分钟让你了解什么是函数栈帧的创建和销毁
1.2 new操作符 1 2 3 4 5 6 int * p = new int (10 );delete p; int * parr = new int [10 ];delete[] arr; 12345
测试delete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int * funcation () { int * p = new int (10 ); return p; } int main () { int * p = funcation(); cout << *p << endl ; cout << *p << endl ; delete p; cout << *p << endl ; return 0 ; } 1234567891011121314
二、引用 2.1 引用基本使用和注意事项 作用 :给变量起个别名。 语法 :数据类型
&
别名
=原名
本质 :指针常量
1 2 3 4 int a = 10 ;int & b = a;123
🔴注意 : ①引用必须初始化。 ②引用一旦初始化就不可以更改了(如下的例子相当于int *const b=&a
)
1 2 3 4 5 6 7 8 9 10 int a = 10 ; int & b; int a = 10 ; int c = 20 ; int & b = a; int & b = c; 123456789
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 void swap1 (int * a, int * b) { int temp = *a; *a = *b; *b = temp; } void swap2 (int & a, int & b) { int temp = a; a = b; b = temp; } int main () { int a = 10 ; int b = 20 ; swap1(&a, &b); swap2(a, b); return 0 ; } 12345678910111213141516171819202122232425
2.3 引用函数返回值 🔴注意 : ①不要返回局部变量的引用。函数返回时,局部变量会被释放,引用或指针指向的内容会失效 ②函数的调用可以作为左值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int & test1 () { int a = 10 ; return a; } int main () { int & ret = test1 (); cout << "ret=" << ret << endl; cout << "ret=" << ret << endl; test1 () = 20 ; cout << "ret=" << ret << endl; cout << "ret=" << ret << endl; return 0 ; } 1234567891011121314151617
2.3 常量引用 目的 :用来修饰形参,防止误操作。
1 2 3 4 5 6 7 8 int &ret=10 ; 1 const int & ret = 10 ;12345
🔴注意 :用常量引用之后不可以更改数据。
1 2 3 4 5 6 7 8 9 int main () { const int & ret = 10 ; ret = 100 ; cout << "ret=" << ret << endl ; return 0 ; } 12345678
C++推荐引用,因为语法方便,编译器帮我们做了指针的内容。
三、函数提高 3.1 函数默认参数 语法 :返回类型
函数名
(参数 =默认值)
{}
用法 :如果自己传入数据就用自己的,如果没有就用默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int Add (int a = 0 , int b = 0 ) { return a + b; } int main () { int a = 10 ; int b = 20 ; int c = Add (a, b); int e = Add (a); int f = Add (b); int g = Add (); cout << "c=" << c << endl; cout << "e=" << e << endl; cout << "f=" << f << endl; cout << "g=" << g << endl; return 0 ; } 123456789101112131415161718
🔴注意 :
①默认值必须放在右边。
1 2 int test (int a, int b = 10 , int c) ;1
②声明和实现,有且只能有一个有默认参数。否则可能出现二义。
3.2 函数占位参数 语法 :返回类型
函数名
(数据类型)
{}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void test1 (int a, int ) { cout << "haha" << endl ; } void test2 (int a, int b=10 ) { cout << "haha" << endl ; } int main () { test1(1 ); test1(1 , 1 ); test2(1 ); test2(1 , 1 ); return 0 ; } 12345678910111213141516171819
3.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 void test () { cout << "调用test( )" << endl ; } void test (int a) { cout << "调用test(int a)" << endl ; } void test (double a) { cout << "调用test(double a)" << endl ; } void test (int a, int b) { cout << "调用test(int a, int b)" << endl ; } void test (int a, double b) { cout << "调用test(int a, double b)" << endl ; } void test (double a, int b) { cout << "调用test(double a, int b)" << endl ; } int main () { test(); test(1 ); test(3.14 ); test(1 ,3.14 ); test(3.14 , 1 ); return 0 ; } 1234567891011121314151617181920212223242526272829303132333435
🔴注意 : ①函数的返回值不可以作为函数重载的条件!出现二义。
1 2 3 4 5 6 7 8 9 10 void test (int a) { cout << "调用test(int a)" << endl ; } int test (int a) { cout << "调用test(int a)" << endl ; } 123456789
②引用作为重载条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void test (int &a) { cout << "调用test(int &a)" << endl ; } void test (const int & b) { cout << "调用test(const int& b)" << endl ; } int main () { int a = 10 ; const int b = 10 ; test(a); test(b); return 0 ; } 123456789101112131415161718
③函数重载碰到默认参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void test (int a,int b=10 ) { cout << "调用test(int &a)" << endl ; } void test (int a) { cout << "调用test(const int& b)" << endl ; } int main () { test(10 ); return 0 ; } 123456789101112131415
四、类与对象 C++面向对象三大特性:封装、继承、多态
4.1 封装 🟦意义 : ①将属性和行为作为一个整体。(放在一个class
里面) ②将属性和行为加以权限控制。 public
公共权限:类内外都可以访问 protected
保护权限: 类外不可以访问 private
私有权限: 类外不可以访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #define PI 3.14 class circle { public : int r; double circumference ( ) { return r * PI * 2 ; } }; int main () { circle c1; c1. r = 10 ; cout << "圆的周长为:" << c1. circumference () << endl; return 0 ; } 12345678910111213141516171819202122 class student { public : string name; int id; void show () { cout << "姓名:" << name << "学号:" << id << endl; } }; int main () { student s1; s1. name = "xiyang" ; s1. id = 1 ; s1. show (); return 0 ; } 1234567891011121314151617181920 class person { public : string name; protected : string car; private : int password; public : void test () { name = "zyz" ; car = "ofo" ; password = 123 ; } }; int main () { person p1; p1. name = "xiyang" ; p1. car = "ufo" ; p1. password = 456 ; return 0 ; } 12345678910111213141516171819202122232425
🔴注意 :struct
与class
的区别:struct
默认权限为:共有class
默认权限为:私有
4.1.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 class person { private : string name; int age; string lover; public : void SetName (string s) { name = s; } string GetName () { return name; } int GetAge () { age = 18 ; return age; } void SetLover (string s) { lover = s; } }; int main () { person p1; p1. SetName ("xiyang" ); p1. SetLover ("薇尔莉特·伊芙加登" ); cout << "姓名为:" << p1. GetName () << endl; cout << "年龄为:" << p1. GetAge () << endl; return 0 ; } 1234567891011121314151617181920212223242526272829303132333435
设计案例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 class cube { private : int L; int W; int H; public : void SetL (int a) { L = a; } int GetL () { return L; } void SetW (int a) { W = a; } int GetW () { return W; } void SetH (int a) { H = a; } int GetH () { return H; } int S () { return 2 * ((L * W) + (L * H) + (W * H)); } int V () { return L * W * H; } bool isSameByClass (cube& c) { if (c.GetL () == L && c.GetW () ==W && c.GetH () == H) 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,c2; c1. SetL (10 ); c1. SetW (10 ); c1. SetH (10 ); c2. SetL (10 ); c2. SetW (5 ); c2. SetH (5 ); cout << "第一个立方体的面积为:" << c1. S () << endl; cout << "第一个立方体的体积为:" << c1. V () << endl; bool ret1 = isSame (c1, c2); if (ret1) { cout << "全局函数判断c1 c2相等" << endl; } else cout << "全局函数判断c1 c2不相等" << endl; bool ret2 = c1. isSameByClass (c2); if (ret2) { cout << "成员函数判断c1 c2相等" << endl; } else cout << "成员函数判断c1 c2不相等" << endl; return 0 ; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
设计案例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 class circle { private : int r; int x; int y; public : void SetX (int a) { x = a; } int GetX () { return x; } void SetY (int a) { y = a; } int GetY () { return y; } void SetR (int a) { r = a; } int GetR () { return r; } }; class point { private : int x; int y; public : void SetX (int a) { x = a; } void SetY (int a) { y = a; } int location (circle& c) { if ((x - c.GetX ()) * (x - c.GetX ()) + (y - c.GetY ()) * (y - c.GetY ()) == c.GetR () * c.GetR ()) return 0 ; else if ((x - c.GetX ()) * (x - c.GetX ()) + (y - c.GetY ()) * (y - c.GetY ()) > c.GetR () * c.GetR ()) return 1 ; else return -1 ; } }; int main () { circle c; point p; c.SetX (0 ); c.SetY (0 ); c.SetR (1 ); p.SetX (0 ); p.SetY (0 ); int ret = p.location (c); if (ret == 1 ) { cout << "点在圆外" << endl; } else if (ret == -1 ) { cout << "点在圆内" << endl; } else { cout << "点在圆上" << endl; } return 0 ; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
优化 :因为圆类里面包含点类(转到目录4.3.2.4类对象作为类的成员)
4.2 对象的初始化和清理 C++利用构造函数和析构函数解决了对象的初始化和清理。对象的初始化和清理工作是编译器强制要求我们做的事情,因此就算我们不提供构造和析构,编译器也会提供,只不过编译器提供的是构造函数和析构函数的空实现。
4.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 class person { public : person () { cout << "无参构造函数调用" << endl; } person (int a) { cout << "有参构造函数调用" << endl; } person (const person &p) { age = p.age; cout << "拷贝构造函数的调用" << endl; } public : int age; }; int main () { person p1; person p2 (10 ) ; person p3 (p2) ; person p4 = person (); person p5 = person (10 ); person p6 = person (p5); person p7 = 10 ; } 12345678910111213141516171819202122232425262728293031323334353637383940
构造函数的调用规则 : 创建一个类,C++至少给每一个类添加4个函数:默认构造(空实现),析构函数(空实现),拷贝构造(值拷贝),赋值运算符Operator=对属性进行值拷贝(4.5.4中介绍) ①如果用户定义一个有参构造函数,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 class person { public : person (int a) { age = a; cout << "默认构造函数的调用" << endl; } int show () { return age; } private : int age; }; int main () { person p1; person p2 (18 ) ; person p3 (p2) ; cout << "p2的年龄为:" << p2. show () << endl; return 0 ; } 123456789101112131415161718192021222324 class person { public : person (const person& p) { age = p.age; cout << "拷贝构造函数的调用" << endl; } int show () { return age; } private : int age; }; int main () { person p1; person p2 (18 ) ; person p3 (p1) ; cout << "p2的年龄为:" << p2. show () << endl; return 0 ; } 123456789101112131415161718192021222324
4.3.2 析构函数 定义 :主要作用在于对象销毁前系统自动调用,执行一些清理工作。 语法 :~类名
()
{ }
🔴注意 : ①析构函数不可以有参数,因此不可以重载。 ②程序在对象销毁前会自动调用析构,无须手动调用,且只用调用一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class person { public : person () { cout << "构造函数的调用" << endl; } ~person () { cout << "析构函数的调用" << endl; } }; int main () { person p; return 0 ; } 123456789101112131415161718
4.3.2.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 41 42 43 44 45 46 class person { public : person () { cout << "默认构造函数的调用" << endl; } person (int a) { age = a; cout << "默认构造函数的调用" << endl; } person (const person& p) { age = p.age; cout << "拷贝构造函数的调用" << endl; } int show () { return age; } private : int age; }; void test1 (person p) {} person test2 () { person p; return p; } int main () { person p1 (20 ) ; person p2 (p1) ; cout << "p2的年龄为:" << p2. show () << endl; person p3; test1 (p3); test2 (); return 0 ; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445
4.3.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 40 41 42 43 44 45 46 47 class person { public : person (int a,int h) { age = a; height = new int (h); cout << "有参构造函数的调用" << endl; } person (const person& p) { age = p.age; height = new int (*(p.height)); cout << "拷贝构造函数的调用" << endl; } ~person () { if (height != NULL ) { delete height; height = NULL ; } cout << "析构函数的调用" << endl; } int GetAge () { return age; } int GetHeight () { return *height; } private : int age; int *height; }; int main () { person p1 (18 , 160 ) ; cout << "p1的年龄为:" << p1. GetAge () << " p1的身高为:" << p1. GetHeight () << endl; person p2 (p1) ; cout << "p2的年龄为:" << p2. GetAge () << " p2的身高为:" << p2. GetHeight () << endl; return 0 ; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546
4.3.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 class person { public : person () :age (10 ), key (123 ), height (160 ) { } person (int a, int b, int c) :age (a), key (b), height (c) { } int age; int key; int height; }; int main () { person p1; person p2 (18 , 456 , 180 ) ; cout << "p1的年龄,密码,身高为:\n" << p1. age << p1. key << p1. height<<endl; cout << "p2的年龄,密码,身高为:\n" << p2. age << p2. key << p2. height<<endl; return 0 ; } 12345678910111213141516171819202122232425
4.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 class phone { public : phone (string p):PhoneName (p) { } string PhoneName; }; class person { public : person (string s,string p) : name (s),Phone (p) { } string name; phone Phone; }; int main () { person p1 ("xiyang" , "huawei" ) ; cout << "名字:" << p1. name << "手机:" << p1. Phone.PhoneName << endl; return 0 ; } 123456789101112131415161718192021222324252627
🔴注意 :若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 class phone { public : phone (string p):PhoneName (p) { cout << "调用phone构造" << endl; } ~phone () { cout << "调用phone析构" << endl; } string PhoneName; }; class person { public : person (string s,string p) : name (s),Phone (p) { cout << "调用person构造" << endl; } ~person () { cout << "调用person析构" << endl; } string name; phone Phone; }; int main () { person p1 ("xiyang" , "huawei" ) ; cout << "名字:" << p1. name << "手机:" << p1. Phone.PhoneName << endl; return 0 ; } 1234567891011121314151617181920212223242526272829303132333435
4.3.5 静态成员
静态成员变量: 🔵特点 : ①所有对象共享一份数据。 ②在编译阶段分配内存。 ③类内声明,类外初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class person { public : static int a; }; int person::a = 100 ;int main () { person p1; cout << "p1的值为:" << p1. a << endl; person p2; p2. a = 200 ; cout << "p1的值为:" << p1. a << endl; return 0 ; } 12345678910111213141516
🔴注意 : ① 静态成员变量不属于某一个对象。因此有两种访问方式:①类名访问,②对象访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class person { public : static int a; }; int person::a = 100 ;int main () { person p1; cout << p1. a << endl; cout << person::a << endl; return 0 ; } 123456789101112131415
②静态成员变量也有访问权限。
静态成员函数: 🔵特点 : ①所有对象共享一个函数。 ②静态成员函数只能访问静态成员函数。 🔴注意 :静态成员函数也有访问权限。
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 class person { public : static void test () { cout << "static void test()调用" << endl; } }; int main () { person p; p.test (); person::test (); return 0 ; } 123456789101112131415161718 class person { public : static void test () { a = 200 ; b = 200 ; cout << "static void test()调用" << endl; } static int a; int b; }; int person::a = 100 ;int main () { person p; p.test (); return 0 ; } 1234567891011121314151617181920
4.3 C++对象模型和this指针 4.4.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 41 42 43 44 45 class person1 { }; class person2 { int a; }; class person3 { int a; static int b; }; class person4 { int a; static int b; void test () { } }; int main () { person1 p1; cout << "sizeof(p)=" << sizeof (p1) << endl; person2 p2; cout << "sizeof(p)=" << sizeof (p2) << endl; person3 p3; cout << "sizeof(p)=" << sizeof (p3) << endl; person4 p4; cout << "sizeof(p)=" << sizeof (p4) << endl; return 0 ; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344
4.4.2 this指针概念 定义 :this指针指向被调用的成员函数所属的对象。 🔵特点 : ①this指针隐含在每一个非静态成员函数内的一种指针。 ②this指针不需要定义,可直接使用。 🟦意义 : ①当形参和成员变量同名时,可以用this指针来区分。 ②在类的非静态成员函数中返回对象本身,可以用retrun *this
❗形参和成员变量名字相同 :
✅解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class person { public : person (int age) { this ->age = age; } int age; }; int main () { person p1 (18 ) ; cout << "p1的年龄为:" << p1. age << endl; return 0 ; } 123456789101112131415161718
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 class person { public : person (int age) { this ->age = age; } person& test (person p) { this ->age += p.age; return *this ; } int age; }; int main () { person p1 (18 ) ; person p2 (18 ) ; p2. test (p1).test (p1); cout << "p2的年龄为:" << p2. age << endl; return 0 ; } 123456789101112131415161718192021222324
4.4.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 25 26 27 28 class person { public : void test1 () { cout << "test1" << endl; } void test2 () { cout << "age=" <<this ->age<< endl; } int age; }; int main () { person *p=NULL ; p->test1 (); p->test2 (); return 0 ; } 1234567891011121314151617181920 void test2 () { if (this ->age==NULL ) return ; cout << "age=" <<this ->age<< endl; } 123456
4.4.4 const修饰成员函数 常函数 :成员函数后const
🔵特点 : ①常函数内不可以修改成员属性。 ②成员属性声明时加关键字mutable
后,在常函数中依然可以修改。
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 person { void test2 () { a = 100 ; b = 200 ; c = 300 ; } void test1 () const { a = 100 ; b = 200 ; c = 300 ; } int a; const int b; mutable int c; }; int main () { person p; p.test1 (); p.test2 (); return 0 ; } 123456789101112131415161718192021222324252627
常对象 :声明对象前加const
🔵特点 : ①常对象只能调用常函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class person { public : void test1 () { } void test2 () const { } int a; const int b; mutable int c; }; int main () { const person p; p.a = 100 ; p.b = 200 ; p.c = 300 ; p.test1 (); p.test2 (); } 1234567891011121314151617181920212223
4.4 友元 目的 :让一个函数或类,访问另一个类中的私有成员。 关键字 :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 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 class building { friend void test2 (building* b) ; private : string bedroom; public : building () { bedroom = "卧室" ; livingroom = "客厅" ; } string livingroom; }; void test1 (building* b) { cout << "访问" << b->livingroom << endl; cout << "访问" << b->bedroom<< endl; } void test2 (building* b) { cout << "访问" << b->livingroom << endl; cout << "访问" << b->bedroom << endl; } int main () { building b1; test1 (&b1); test2 (&b1); return 0 ; } 12345678910111213141516171819202122232425262728293031 class building { friend class gay2 ; public : building (); string livingroom; private : string bedroom; }; building::building () { bedroom = "卧室" ; livingroom = "客厅" ; } class gay1 { public : gay1 (); void vist () ; building* b; }; gay1::gay1 () { b = new building; } void gay1::vist () { cout << "朋友在访问" << b->livingroom << endl; cout<< "朋友在访问" << b->bedroom << endl; } class gay2 { public : gay2 (); void vist () ; building* b; }; gay2::gay2 () { b = new building; } void gay2::vist () { cout << "朋友在访问" << b->livingroom << endl; cout << "朋友在访问" << b->bedroom << endl; } int main () { gay1 g1; g1. vist (); gay2 g2; g2. vist (); return 0 ; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 class building ;class gay { public : gay (); void test1 () ; void test2 () ; building* b; }; gay::gay () { b = new building; } void gay::test1 () { cout << "正在访问" << b->livingroom <<endl; cout << "正在访问" << b->bedroom << endl; } void gay::test2 () { cout << "正在访问" << b->livingroom << endl; cout << "正在访问" << b->bedroom << endl; } class building { public : building (); string livingroom; friend void gay::test2 () ; private : string bedroom; }; building::building () { bedroom = "卧室" ; livingroom = "客厅" ; } int main () { gay g1; g1. test1 (); g1. test2 (); return 0 ; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546
4.5 运算符重载 运算符重载对已有的运算符重新定义,赋予另一种功能,以适应不同的数据类型。
4.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 class person { public : person (int x, int y) :a (x), b (y) { } person operator +(person& p) { person temp (0 , 0 ) ; temp.a = this ->a + p.a; temp.b = this ->b + p.b; return temp; } int a; int b; }; person operator +(person& p1, person& p2) { person temp (0 , 0 ) ; temp.a = p1. a + p2. a; temp.b = p1. b + p2. b; return temp; } int main () { person p1 (10 , 10 ) ; person p2 (10 , 10 ) ; person p3 = p1+p2; person p3 = p1. operator +(p2); person p3 = operator +(p1, p2); } 12345678910111213141516171819202122232425262728293031323334353637
🔴注意 :不要滥用运算重载符。
4.5.2 左移运算符重载 作用 :可以输出自定义类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class person { public : person (int x) :a (x) { } int a; int b; }; ostream& operator <<(ostream& cout, person& p) { cout <<"p中a的值为:" << p.a<<"p中b的值为:" << p.b<<endl; return cout; } int main () { person p (10 ) ; cout << p; operator <<(cout, p); return 0 ; } 12345678910111213141516171819202122232425262728293031
4.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 class MyInt { friend ostream& operator <<(ostream& cout, MyInt m); public : MyInt () { num = 0 ; } MyInt& operator ++() { num++; return *this ; } MyInt operator ++(int ) { MyInt temp = *this ; num++; return temp; } private : int num; }; ostream& operator <<(ostream& cout, MyInt m) { cout << m.num; return cout; } int main () { MyInt m; cout << ++m << endl; cout <<m++<< endl; return 0 ; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546
🔴注意 :前置++,返回是引用。后置++,返回的是值。
4.5.4 赋值运算符重载 创建一个类,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 class person { public : person (int a) { age=new int (a); } ~person () { if (age != NULL ) { delete age; age = NULL ; } } person& operator =(person& p) { if (age != NULL ) { delete age; age = NULL ; } age = new int (*p.age); return *this ; } int *age; }; int main () { person p1 (10 ) ; person p2 (20 ) ; person p3 (30 ) ; p3=p2 = p1; cout << *(p2. age) << endl; cout << *(p3. age) << endl; return 0 ; } 1234567891011121314151617181920212223242526272829303132333435363738394041
4.5.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 class person { public : person (string s,int a):name (s),age (a) { } bool operator ==(person& p) { if (this ->name == p.name && this ->age == p.age) { return true ; } else return false ; } string name; int age; }; int main () { person p1 ("xi" ,10 ) ; person p2 ("xi" ,20 ) ; if (p1 == p2) { cout << "p1和p2相等" << endl; } else cout << "p1和p2不相等" << endl; return 0 ; } 1234567891011121314151617181920212223242526272829303132
4.5.6 函数调用运算符重载(仿函数) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class MyPrint { public : void operator () (string text) { cout << text << endl; } }; void print (string text) { cout << text << endl; } int main () { MyPrint m1,m2; m1 ("xiyang" ); m2 ("xiyang" ); return 0 ; } 1234567891011121314151617181920
4.6 继承 4.6.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 139 140 141 142 143 class java { public : void head () { cout << "首页、公开课、登录、注册...(公共头部)" << endl; } void foot () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content () { cout << "JAVA学科视频" << endl; } }; class cpp { public : void head () { cout << "首页、公开课、登录、注册...(公共头部)" << endl; } void foot () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content () { cout << "CPP学科视频" << endl; } }; class python { public : void head () { cout << "首页、公开课、登录、注册...(公共头部)" << endl; } void foot () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content () { cout << "python学科视频" << endl; } }; void test1 () { cout << "java下载视频页面如下:" << endl; java j; j.head (); j.foot (); j.left (); j.content (); cout << "--------------------" << endl; } void test2 () { cout << "cpp下载视频页面如下:" << endl; cpp c; c.head (); c.foot (); c.left (); c.content (); cout << "--------------------" << endl; } void test3 () { cout << "python下载视频页面如下:" << endl; python p; p.head (); p.foot (); p.left (); p.content (); cout << "--------------------" << endl; } int main () { test1 (); test2 (); test3 (); return 0 ; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 class BasePage { public : void head () { cout << "首页、公开课、登录、注册...(公共头部)" << endl; } void foot () { cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; } void left () { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content () { cout << "python学科视频" << endl; } }; class java :public BasePage{ void content () { cout << "java学科视频" << endl; } }; class cpp :public BasePage{ void content () { cout << "cpp学科视频" << endl; } }; class python :public BasePage{ void content () { cout << "python学科视频" << endl; } }; 123456789101112131415161718192021222324252627282930313233343536373839404142
优势 :减少重复代码 语法 :class 子类 : 继承方式
4.6.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 class father { public : int a; protected : int b; private : int c; }; class son1 :public father{ public : void test1 () { a = 10 ; b = 10 ; } }; void test11 () { son1 s; s.a; } class son2 :protected father{ public : void test2 () { a = 10 ; b = 10 ; } }; void test22 () { son2 s; } class son3 :private father{ public : void test3 () { a = 10 ; b = 10 ; } }; void test22 () { son3 s; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
4.6.3 继承中的对象模型 ❗ Q :从父类继承过来的成员,哪些属于子类对象中?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class father { public : int a; static int d; protected : int b; private : int c; }; class son :public father{ public : int e; }; int main () { cout << "size of(son)=" << sizeof (son) << endl; return 0 ; } 1234567891011121314151617181920212223
用工具查看 :
1 2 cl /d1 reportSingleClassLayout查看的类名 所属文件名 1
4.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 class father { public : father () { cout << "father的构造函数" << endl; } ~father () { cout << "father的析构函数" << endl; } }; class son :public father{ public : son () { cout << "son的构造函数" << endl; } ~son () { cout << "son的析构函数" << endl; } }; int main () { son s; return 0 ; } 123456789101112131415161718192021222324252627282930
4.6.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 52 class father { public : father () { a = 10 ; } void test () { cout << "father中的test()函数调用" << endl; } int a; static int b; }; int father::b=10 ;class son :public father{ public : son () { a = 20 ; } void test () { cout << "son中的test()函数调用" << endl; } int a; static int b; }; int son::b = 20 ;int main () { son s; cout << "son中a=" << s.a << endl; cout << "father中a=" << s.father::a << endl; s.test (); s.father::test (); cout << "对象访问son中b=" << s.b << endl; cout << "对象访问father中b=" << s.father::b << endl; cout << "类名访问son中b = " << son::b << endl; cout << "类名访问father中b = " << father::b << endl; cout << "类名访问father中b = " << son::father::b << endl; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
①子类对象访问子类同名成员:直接访问 ②子类对象访问父类同名成员:加作用域::
4.6.6 多继承语法 语法 :class 子类 :继承方式 父类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 class father1 { public : father1 () { a = 10 ; } int a; }; class father2 { public : father2 () { b = 10 ; } int b; }; class son :public father1, public father2{ public : son () { c = 10 ; d = 20 ; } int c; int d; }; int main () { son s; cout << "sizeof (son)=" << sizeof (son) << endl; cout << "father1中a的值" << s.a << endl; } 12345678910111213141516171819202122232425262728293031323334353637
🔴注意 :C++实际开发中不建议用多继承
4.6.7 菱形继承 定义 :两个派生类继承同一个基类,又有某个类同时继承者两个派生类。 ❗Q :菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义 ✅:虚继承可以解决菱形继承问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class animal { public : int age; }; class yang :virtual public animal{};class tuo :virtual public animal{};class YangTuo :public yang, public tuo{};int main () { YangTuo yt; yt.yang::age = 10 ; yt.tuo::age = 20 ; cout << yt.yang::age << endl; cout << yt.tuo::age << endl; cout << yt.age << endl; } 1234567891011121314151617181920
4.7 多态 4.7.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 class animal { public : void breathe () { cout << "动物呼吸" << endl; } virtual void speak () { cout << "动物叫" << endl; } }; class cat :public animal{ public : void breathe () { cout << "喵呼吸" << endl; } void speak () { cout << "miaomiao~" << endl; } }; void DoSpeak (animal &a) { a.speak (); } void DoBreathe (animal& a) { a.breathe (); } int main () { cat c; DoSpeak (c); DoBreathe (c); } 1234567891011121314151617181920212223242526272829303132333435363738394041424344
动态多态满足条件: ①有继承关系 ②子类重写父类中的虚函数(子类的virtual
可有可无)
动态多态的使用 :父类的指针或引用,执行子类对象。
4.7.2 多态原理剖析(图解)
4.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 class calculator { public : int GetResult (string s) { if (s == "+" ) return a + b; else if (s == "-" ) return a - b; else if (s == "*" ) return a * b; else if (s == "/" ) return a / b; } int a; int b; }; int main () { calculator c; string s; cout << "输入两个整型数字的+-*/运算:" << endl;; cin >> c.a >>s>> c.b; cout << "=" << c.GetResult (s) << endl; return 0 ; } 123456789101112131415161718192021222324252627
❗局限 :扩展新的功能,需要修改源码。 ✅:
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 class AbstractCalculator { public : virtual int GetResult () { return 0 ; } int a; int b; }; class Add :public AbstractCalculator{ public : int GetResult () { return a + b; } }; class Sub :public AbstractCalculator{ public : int GetResult () { return a - b; } }; class Mul :public AbstractCalculator{ public : int GetResult () { return a * b; } }; class Div :public AbstractCalculator{ public : int GetResult () { return a / b; } }; void Do (AbstractCalculator& ac2) { ac2. GetResult (); } int main () { AbstractCalculator* ac = new Add; ac->a = 10 ; ac->b = 20 ; cout << ac->a << "+" << ac->b << "=" << ac->GetResult () << endl; delete ac; ac = new Sub; ac->a = 10 ; ac->b = 20 ; cout << ac->a << "-" << ac->b << "=" << ac->GetResult () << endl; delete ac; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
多态优势 : ①组织结构清晰,可读性强。 ②对于前期和后期扩展及维护性高。
4.7.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 class base { public : virtual void func () = 0 ; }; class son1 :public base{ public :}; class son2 :public base{ public : void func () {}}; int main () { son2 s; } 123456789101112131415161718192021222324
4.7.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 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 class AbstractDrinking { public : virtual void BoilWater () = 0 ; virtual void Brew () = 0 ; virtual void IntoCup () = 0 ; virtual void PutSomeing () = 0 ; void Make () { BoilWater (); Brew (); IntoCup (); PutSomeing (); } }; class Tea :public AbstractDrinking{ virtual void BoilWater () { cout << "煮500ml水" << endl; } virtual void Brew () { cout << "加入10g茶叶" << endl; } virtual void IntoCup () { cout<<"倒入杯子里" <<endl; } virtual void PutSomeing () { cout << "加入柠檬" << endl; } }; class Coffee :public AbstractDrinking{ virtual void BoilWater () { cout << "煮500ml水" << endl; } virtual void Brew () { cout << "加入一勺咖啡" << endl; } virtual void IntoCup () { cout << "倒入杯子里" << endl; } virtual void PutSomeing () { cout << "加入牛奶" << endl; } }; void DoMake (AbstractDrinking* ad) { ad->Make (); delete ad; } int main () { DoMake (new Tea); DoMake (new Coffee); } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
4.7.6 虚析构函数和抽象类 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码,将父类中的析构函数改为虚析构 或者纯虚析构 就可以解决
虚析构语法 :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 class Animal { public : Animal () { cout << "Animal构造函数的调用" << endl; } ~Animal () { cout << "Animal析构函数的调用" << endl; } virtual void speak () = 0 ; }; class Cat :public Animal{ public : Cat (string n) { cout << "Cat的构造函数的调用" << endl; name = new string (n); } void speak () { cout << *name<<"miao~" << endl; } ~Cat () { cout << "Cat的析构函数的调用" << endl; if (name != NULL ) { delete name; name = NULL ; } } string *name; }; int main () { Animal* a = new Cat ("mimi" ); a->speak (); delete a; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
虚析构和纯虚析构共性 : ①可以解决父类指针释放子类对象 ②都需要有具体的函数实现
🔴注意 :纯虚析构,该类属于抽象类,无法实例化对象
4.7.7 多态案例三:电脑组装 案例描述 : 电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储) 将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商 创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口 测试时组装三台不同的电脑进行工作
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 class CPU { public : virtual void calulate () = 0 ; }; class VideoCard { public : virtual void display () = 0 ; }; class Memory { public : virtual void storage () = 0 ; }; class IntelCPU :public CPU{ void calulate () { cout << "Intel的CPU工作啦!" << endl; } }; class IntelVideoCard :public VideoCard{ void display () { cout << "Intel的VideoCard工作啦!" << endl; } }; class IntelMemory :public Memory{ void storage () { cout << "Intel的Memory工作啦!" << endl; } }; class XiaoMiCPU :public CPU{ void calulate () { cout << "XiaoMi的CPU工作啦!" << endl; } }; class XiaoMiVideoCard :public VideoCard{ void display () { cout << "XiaoMi的VideoCard工作啦!" << endl; } }; class XiaoMiMemory :public Memory{ void storage () { cout << "XiaoMi的Memory工作啦!" << endl; } }; class Computer { public : Computer (CPU* cpu, VideoCard* videpcard, Memory* memory) { this ->cpu = cpu; this ->videpcard = videpcard; this ->memory = memory; } void DoWork () { cpu->calulate (); videpcard->display (); memory->storage (); } ~Computer () { if (cpu!= NULL ) { delete cpu; cpu = NULL ; } if (videpcard!= NULL ) { delete videpcard; videpcard = NULL ; } if (memory!= NULL ) { delete memory; memory = NULL ; } } private : CPU* cpu; VideoCard* videpcard; Memory* memory; }; int main () { CPU* intel_cpu = new IntelCPU; VideoCard* intel_vc = new IntelVideoCard; Memory* intel_m = new IntelMemory; Computer c1 (intel_cpu, intel_vc, intel_m) ; c1. DoWork (); CPU* xiaomi_cpu = new XiaoMiCPU; VideoCard* xiaomi_vc = new XiaoMiVideoCard; Memory* xiaomi_m = new XiaoMiMemory; Computer c2 (xiaomi_cpu, xiaomi_vc, xiaomi_m) ; c2. DoWork (); } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
五、文件 5.1 文本文件 程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放,通过文件 可以将数据持久化 C++中对文件操作需要包含头文件< fstream >
文件类型分为两种: ①文本文件 - 文件以文本的ASCII码 形式存储在计算机中 ②二进制文件 - 文件以文本的二进制形式 存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类: ①ofstream:写操作 ②ifstream: 读操作 ③fstream : 读写操作
5.1.1 写文件 写文件步骤如下 :
包含头文件: #include <fstream>
创建流对象: ofstream ofs;
打开文件: ofs.open(“文件路径”,打开方式);
写数据: ofs << "写入的数据";
关闭文件:ofs.close();
文件打开方式 :
打开方式
解释
ios::in
为读文件而打开文件
ios::out
为写文件而打开文件
ios::ate
初始位置:文件尾
ios::app
追加方式写文件
ios::trunc
如果文件存在先删除,再创建
ios::binary
二进制方式
🔴注意 :文件打开方式可以配合使用,利用|
操作符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <fstream> int main () { ofstream ofs; ofs.open ("test.txt" , ios::out); ofs << "姓名:张三" << endl; ofs << "性别:男" << endl; ofs << "年龄:18" << endl; ofs.close (); return 0 ; } 1234567891011121314151617
5.1.2 读文件 读文件步骤如下 :
包含头文件: #include <fstream>
创建流对象: ifstream ifs;
打开文件并判断文件是否打开成功: ifs.open(“文件路径”,打开方式);
读数据: 四种方式读取
关闭文件: ifs.close();
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 <fstream> #include <string> void test () { ifstream ifs; ifs.open ("test.txt" , ios::in); if (!ifs.is_open ()) { cout << "文件打开失败" << endl; return ; } 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 (getline (ifs, buf)) { cout << buf << endl; } char c; while ((c = ifs.get ()) != EOF) { cout << c; } ifs.close (); } int main () { test (); return 0 ; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
5.2 二进制文件 以二进制的方式对文件进行读写操作打开方式要指定为 ios::binary
5.2.1 写文件 二进制方式写文件主要利用流对象调用成员函数write
函数原型 :ostream& write(const char * buffer,int len);
参数解释 :字符指针buffer
指向内存中一段存储空间。len
是读写的字节数
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 <fstream> #include <string> class Person { public : char m_Name[64 ]; int m_Age; }; void test01 () { ofstream ofs ("person.txt" , ios::out | ios::binary) ; Person p = { "张三" , 18 }; ofs.write ((const char *)&p, sizeof (p)); ofs.close (); } int main () { test01 (); return 0 ; } 1234567891011121314151617181920212223242526
5.2.2 读文件 二进制方式读文件主要利用流对象调用成员函数read
函数原型 :istream& read(char *buffer,int len);
参数解释 :字符指针buffer
指向内存中一段存储空间。len
是读写的字节数
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 <fstream> #include <string> class Person { public : char m_Name[64 ]; int m_Age; }; void test01 () { ifstream ifs ("person.txt" , ios::in | ios::binary) ; if (!ifs.is_open ()) { cout << "文件打开失败" << endl; return ; } Person p; ifs.read ((char *)&p, sizeof (p)); cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl; ifs.close (); } int main () { test01 (); return 0 ; } 12345678910111213141516171819202122232425262728293031
文章知识点与官方知识档案匹配,可进一步学