本文最后更新于 766 天前,其中的信息可能已经有所发展或是发生改变。
C++
程序在执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理的
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
内存四区意义:
不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
(1) 代码区:
内容:存放CPU执行的机器指令
特点:
- 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
- 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
(2) 全局区:
内容:全局变量和静态变量存放在此.
特点:
- 全局区还包含了常量区, 字符串常量和其他常量也存放在此
- 该区域的数据在程序结束后由操作系统释放
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| |
| int g_a=10; |
| int g_b=10; |
| |
| |
| static int s_g_a=10; |
| static int s_g_b=10; |
| |
| |
| string g_s1="abcd"; |
| string g_s2="abcd"; |
| |
| |
| const int g_c_a=10; |
| const int g_c_b=10; |
| |
| int main(){ |
| |
| cout<<"程序运行前:"<<endl; |
| |
| cout<<endl; |
| |
| |
| int a=10; |
| int b=10; |
| |
| |
| cout<<"局部变量a的地址为:"<<&a<<endl; |
| cout<<"局部变量b的地址为:"<<&b<<endl; |
| |
| |
| cout<<"全局变量g_a的地址为:"<<&g_a<<endl; |
| cout<<"全局变量g_b的地址为:"<<&g_b<<endl; |
| |
| cout<<endl; |
| |
| |
| static int s_a=10; |
| static int s_b=10; |
| |
| |
| cout<<"静态局部变量s_a的地址为:"<<&s_a<<endl; |
| cout<<"静态局部变量s_b的地址为:"<<&s_b<<endl; |
| |
| |
| cout<<"静态全局变量s_g_a的地址为:"<<&s_g_a<<endl; |
| cout<<"静态全局变量s_g_b的地址为:"<<&s_g_b<<endl; |
| |
| cout<<endl; |
| |
| |
| |
| string s1="abcd"; |
| string s2="abcd"; |
| |
| |
| const int c_a=10; |
| const int c_b=10; |
| |
| |
| cout<<"字符串局部常量s1的地址为:"<<&s1<<endl; |
| cout<<"字符串局部常量s2的地址为:"<<&s2<<endl; |
| |
| |
| cout<<"字符串全局常量g_s1的地址为:"<<&g_s1<<endl; |
| cout<<"字符串全局常量g_s2的地址为:"<<&g_s2<<endl; |
| |
| |
| cout<<"const 修饰的局部常量c_a的地址为:"<<&c_a<<endl; |
| cout<<"const 修饰的局部常量c_b的地址为:"<<&c_b<<endl; |
| |
| |
| cout<<"const 修饰的全局常量g_c_a的地址为:"<<&g_c_a<<endl; |
| cout<<"cosnt 修饰的全局常量g_c_b的地址为:"<<&g_c_b<<endl; |
| |
| cout<<endl; |
| |
| cout<<"有全局修饰的在全局区"<<endl; |
| cout<<"其他的不在全局区"<<endl; |
| |
| return 0; |
| |
| } |
总结:
C++
中在程序运行前分为全局区和代码区
- 代码区特点是共享和只读
- 全局区中存放全局变量、静态变量、常量
- 常量区中存放 const修饰的全局常量 和 字符串常量
在程序编译后,生成了exe可执行程序,执行该程序后分为两个区域
(1) 栈区:
- 由编译器自动分配释放, 存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
(2) 堆区:
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int* test_01(){ |
| int a=10; |
| return &a; |
| } |
| |
| int* test_02(){ |
| int* m=new int(10); |
| return m; |
| } |
| |
| int main(){ |
| |
| cout<<"栈区数据由编译器自动分配释放, 存放函数的参数值,局部变量等"<<endl; |
| |
| |
| int* p1=test_01(); |
| |
| cout<<endl; |
| |
| |
| cout<<"第一次输出,编译器对局部变量做一次保留,暂时不释放: "<<*p1<<endl; |
| |
| cout<<"第二次输出,编译器不再保留栈区的数据,直接释放:"<<*p1<<endl; |
| |
| cout<<"不要返回局部变量的地址!!!"<<endl; |
| |
| cout<<endl; |
| |
| |
| int* p2=test_02(); |
| |
| |
| cout<<"输出存放在堆区的数据,编译器不释放,由程序员手动释放: "<<*p2<<endl; |
| cout<<"输出存放在堆区的数据,编译器不释放,由程序员手动释放: "<<*p2<<endl; |
| cout<<"输出存放在堆区的数据,编译器不释放,由程序员手动释放: "<<*p2<<endl; |
| |
| cout<<endl; |
| |
| |
| delete p2; |
| |
| cout<<"程序员手动释放后: "<<*p2<<endl; |
| |
| return 0; |
| |
| } |
总结:
堆区数据由程序员管理开辟和释放
堆区数据利用new
关键字进行开辟内存
C++
中利用new
操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
语法: new 数据类型
利用new
创建的数据,会返回该数据对应的类型的指针
示例1: 开辟数据
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int* test_01(){ |
| int* a=new int(10); |
| return a; |
| } |
| |
| int main(){ |
| |
| int *p=test_01(); |
| |
| cout<<*p<<endl; |
| cout<<*p<<endl; |
| |
| |
| delete p; |
| |
| cout<<*p<<endl; |
| |
| return 0; |
| |
| } |
示例2:开辟数组
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int* test_01(){ |
| int* a=new int[10]; |
| return a; |
| } |
| |
| int main(){ |
| |
| int *p=test_01(); |
| |
| for(int i=0;i<10;i++) p[i]=i+1; |
| |
| for(int i=0;i<10;i++) cout<<p[i]<<" "; |
| |
| cout<<endl; |
| |
| |
| cout<<*p<<endl; |
| |
| |
| delete[] p; |
| |
| |
| cout<<*p<<endl; |
| |
| return 0; |
| |
| } |
作用: 给变量起别名
语法: 数据类型 &别名 = 原名
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int main(){ |
| |
| int a=10; |
| |
| int &b=a; |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| |
| |
| a=20; |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| |
| |
| b=10; |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| |
| return 0; |
| |
| } |
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int main() { |
| |
| int a=10; |
| int b=20; |
| |
| int &c=a; |
| c=b; |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| cout<<"c = "<<c<<endl; |
| |
| return 0; |
| |
| } |
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| void swap(int &a,int &b){ |
| int t=a; |
| a=b; |
| b=t; |
| } |
| |
| int main(){ |
| |
| int a=10; |
| int b=20; |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| |
| cout<<endl; |
| |
| swap(a,b); |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| |
| return 0; |
| |
| } |
总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单
作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int &test_01(){ |
| int a=10; |
| return a; |
| } |
| |
| int &test_02(){ |
| static int a=10; |
| return a; |
| } |
| |
| int main(){ |
| |
| int &ans_01=test_01(); |
| |
| cout<<"ans_01 = "<<ans_01<<endl; |
| cout<<"ans_01 = "<<ans_01<<endl; |
| cout<<"不要返回局部变量的引用!!!"<<endl; |
| |
| cout<<endl; |
| |
| int &ans_02=test_02(); |
| |
| cout<<"ans_02 = "<<ans_02<<endl; |
| cout<<"ans_02 = "<<ans_02<<endl; |
| cout<<"ans_02 = "<<ans_02<<endl; |
| |
| test_02()=20; |
| |
| cout<<"ans_02 = "<<ans_02<<endl; |
| |
| return 0; |
| |
| } |
本质:引用的本质在c++内部实现是一个指针常量.
讲解示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| |
| void test_01(int& ref){ |
| ref=100; |
| } |
| |
| int main(){ |
| |
| int a=10; |
| |
| |
| int& ref=a; |
| ref=20; |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"ref = "<<ref<<endl; |
| |
| test_01(a); |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"ref = "<<ref<<endl; |
| |
| return 0; |
| |
| } |
结论:C++
推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加==const修饰形参==,防止形参改变实参
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int test_01(const int &a){ |
| int b=a+10; |
| |
| return b; |
| } |
| |
| int main(){ |
| |
| int a=10; |
| |
| int b=test_01(a); |
| |
| cout<<"a = "<<a<<endl; |
| cout<<"b = "<<b<<endl; |
| |
| return 0; |
| |
| } |
在C++
中,函数的形参列表中的形参是可以有默认值的。
语法: 返回值类型 函数名 (参数= 默认值){}
示例:
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int add(int a,int b=10){ |
| return a+b; |
| } |
| |
| int main(){ |
| |
| int a=20,b=30; |
| |
| cout<<add(a,b)<<endl; |
| |
| cout<<add(a)<<endl; |
| |
| return 0; |
| } |
C++
中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法: 返回值类型 函数名 (数据类型){}
在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该技术
示例:
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int add(int a,int){ |
| return a; |
| } |
| |
| int main(){ |
| |
| int a=20,b=30; |
| |
| cout<<add(a,b)<<endl; |
| |
| return 0; |
| } |
作用:函数名可以相同,提高复用性
函数重载满足条件:
- 同一个作用域下
- 函数名称相同
- 函数参数类型不同 或者 个数不同 或者 顺序不同
注意: 函数的返回值不可以作为函数重载的条件
示例:
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| int add(){ |
| cout<<"add() 的调用: "<<0<<endl; |
| } |
| |
| int add(int a){ |
| cout<<"add(int a) 的调用:"<<a<<endl; |
| } |
| |
| int add(double a){ |
| cout<<"add(double a) 的调用:"<<a<<endl; |
| } |
| |
| int add(int a,int b){ |
| cout<<"add(int a,int b) 的调用:"<<a<<"+"<<b<<"="<<a+b<<endl; |
| } |
| |
| int add(int a,double b){ |
| cout<<"add(int a,double b) 的调用:"<<a<<"+"<<b<<"="<<a+b<<endl; |
| } |
| |
| int add(double a,int b){ |
| cout<<"add(dpuble a,int b) 的调用:"<<a<<"+"<<b<<"="<<a+b<<endl; |
| } |
| |
| int add(double a,double b){ |
| cout<<"add(dpuble a,double b) 的调用:"<<a<<"+"<<b<<"="<<a+b<<endl; |
| } |
| |
| int main(){ |
| |
| add(); |
| |
| add(1); |
| |
| add(1.11); |
| |
| add(1,2); |
| |
| add(1,2.22); |
| |
| add(1.11,2); |
| |
| add(1.11,2.22); |
| |
| return 0; |
| } |
示例:
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| void func(int &a){ |
| cout<<"func(int &a) 的调用:"<<a<<endl; |
| } |
| |
| void func(const int &a){ |
| cout<<"func(const int &a) 的调用:"<<a<<endl; |
| } |
| |
| int main(){ |
| |
| int a=10; |
| |
| func(a); |
| func(20); |
| |
| return 0; |
| } |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| void func(int a, int b = 10) |
| { |
| cout<<"func2(int a,int b = 10) 的调用"<<endl; |
| } |
| |
| void func(int a) |
| { |
| cout<<"func(int a) 的调用"<<endl; |
| } |
| |
| int main(){ |
| |
| func(10); |
| |
| return 0; |
| |
| } |
C++
面向对象的三大特性为:封装、继承、多态
C++
认为万事万物都皆为对象,对象上有其属性和行为
例如:
人可以作为对象,属性有姓名、年龄、身高、体重...,行为有走、跑、跳、吃饭、唱歌...
车也可以作为对象,属性有轮胎、方向盘、车灯...,行为有载人、放音乐、放空调...
具有相同性质的==对象==,我们可以抽象称为==类==,人属于人类,车属于车类
封装是C++
面向对象三大特性之一
封装的意义:
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
封装意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法: class 类名{ 访问权限: 属性 / 行为 };
示例1:设计一个圆类,求圆的周长
示例代码:
| |
| |
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| const double PI=3.1415926; |
| |
| class circle{ |
| public: |
| |
| |
| int r; |
| |
| |
| double caculate(){ |
| return r*r*PI; |
| } |
| |
| }; |
| |
| int main(){ |
| |
| |
| circle c1; |
| |
| c1.r=10; |
| |
| cout<<"c1的周长为: "<<c1.caculate()<<endl; |
| |
| return 0; |
| |
| } |
封装意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
- public 公共权限
- protected 保护权限
- private 私有权限
示例:
| |
| |
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| |
| |
| string name; |
| |
| protected: |
| |
| |
| int money; |
| |
| private: |
| |
| |
| int year; |
| |
| public: |
| void make(){ |
| name="lys"; |
| money=100000000; |
| year=20; |
| } |
| |
| }; |
| |
| int main(){ |
| |
| |
| person p1; |
| |
| p1.make(); |
| |
| cout<<p1.name; |
| |
| p1.money=0; |
| cout<<p1.money; |
| |
| p1.year=100; |
| cout<<p1.year; |
| |
| return 0; |
| |
| } |
在C++
中 struct和class唯一的区别就在于 默认的访问权限不同
区别:
- struct 默认权限为公共
- class 默认权限为私有
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| struct point_1{ |
| int x,y; |
| }; |
| |
| class point_2{ |
| int x,y; |
| }; |
| |
| int main(){ |
| point_1 p1; |
| point_2 p2; |
| |
| p1.x=10,p1.y=20; |
| |
| p2.x=10,p2.y=20; |
| |
| return 0; |
| } |
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知
同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
解决方法:
c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:类名(){}
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法: ~类名(){}
- 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号 ~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
- 不能设为私有
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| |
| point(){ |
| cout<<"point的构造函数调用"<<endl; |
| } |
| |
| |
| ~point(){ |
| cout<<"point的析构函数调用"<<endl; |
| } |
| |
| }; |
| |
| int main(){ |
| |
| point p1; |
| |
| return 0; |
| |
| } |
两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
示例:
| |
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| |
| int x,y; |
| |
| point(){ |
| cout<<"无参构造函数调用"<<endl; |
| } |
| |
| point(int a,int b){ |
| x=a; |
| y=b; |
| cout<<"有参构造函数调用"<<endl; |
| } |
| |
| point(const point &p){ |
| x=p.x; |
| y=p.y; |
| cout<<"拷贝构造函数调用"<<endl; |
| } |
| |
| }; |
| |
| int main(){ |
| |
| |
| point p1; |
| point p2(1,2); |
| point p3(p2); |
| |
| |
| |
| point p4=point(); |
| point p5=point(3,4); |
| point p6=point(p5); |
| |
| point p7; |
| point p8={5,6}; |
| point p9=p8; |
| |
| return 0; |
| |
| } |
C++
中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| |
| int x,y; |
| |
| point(){ |
| cout<<"默认构造函数调用"<<endl; |
| } |
| |
| point(int a,int b){ |
| x=a; |
| y=b; |
| cout<<"有参函数构造调用"<<endl; |
| } |
| |
| point(const point &p){ |
| x=p.x; |
| y=p.y; |
| cout<<"拷贝构造函数调用"<<endl; |
| } |
| }; |
| |
| |
| void test01(){ |
| |
| point p1(1,2); |
| |
| point p2(p1); |
| |
| } |
| |
| |
| void make(point &p){ |
| p.x=1; |
| p.y=2; |
| } |
| |
| void test02(){ |
| point p3; |
| make(p3); |
| cout<<p3.x<<" "<<p3.y; |
| } |
| |
| |
| int show_x(point p){ |
| return p.x; |
| } |
| |
| void test03(){ |
| point p4(2,3); |
| cout<<show_x(p4); |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| test02(); |
| |
| test03(); |
| |
| return 0; |
| |
| } |
默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(){ |
| cout<<"无参构造函数"<<endl; |
| } |
| |
| point(int a,int b){ |
| x=a; |
| y=b; |
| cout<<"有参构造函数"<<endl; |
| } |
| |
| point(const point &p){ |
| x=p.x; |
| y=p.y; |
| cout<<"拷贝构造函数"<<endl; |
| } |
| |
| ~point(){ |
| cout<<"析构函数"<<endl; |
| } |
| }; |
| |
| void test01(){ |
| |
| point p1(1,2); |
| |
| |
| point p2(p1); |
| |
| printf("p2 = (%d,%d)\n",p2.x,p2.y); |
| |
| } |
| |
| void test02(){ |
| |
| point p3; |
| |
| point p4(3,4); |
| |
| point p5(p4); |
| |
| |
| point p6; |
| |
| point p7(5,6); |
| point p8(p7); |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| test02(); |
| |
| return 0; |
| |
| } |
- 浅拷贝:简单的赋值拷贝操作
- 深拷贝:在堆区重新申请空间,进行拷贝操作
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| int *z; |
| |
| point(int a,int b,int h){ |
| x=a; |
| y=b; |
| z=new int(h); |
| } |
| |
| |
| |
| ~point(){ |
| if(z!=NULL){ |
| delete z; |
| z=NULL; |
| } |
| } |
| |
| }; |
| |
| void test01(){ |
| |
| point p1(1,2,3); |
| |
| point p2(p1); |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| return 0; |
| } |
| |
| |
| |
| |
解决方案
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| int *z; |
| |
| point(int a,int b,int h){ |
| x=a; |
| y=b; |
| z=new int(h); |
| } |
| |
| point(const point &p){ |
| x=p.x; |
| y=p.y; |
| |
| z=new int(*p.z); |
| } |
| |
| ~point(){ |
| if(z!=NULL){ |
| delete z; |
| z=NULL; |
| } |
| } |
| |
| }; |
| |
| void test01(){ |
| |
| point p1(1,2,3); |
| |
| point p2(p1); |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| return 0; |
| } |
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
作用:C++
提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)... {}
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y,z; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| point(int a,int b,int h):x(a),y(b),z(h) {} |
| |
| }; |
| |
| void test01(){ |
| |
| point p1(1,2,3); |
| |
| cout<<p1.x<<" "<<p1.y<<" "<<p1.z<<endl; |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| return 0; |
| } |
C++
类中的成员可以是另一个类的对象,我们称该成员为 对象成员
例如:
| class A{ |
| |
| } |
| |
| class B{ |
| A a; |
| } |
B类中有对象A作为成员,A为对象成员当创建B对象时,A与B的构造和析构的顺序
示例:
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class A{ |
| public: |
| |
| A(){ |
| cout<<"A的构造函数调用"<<endl; |
| } |
| |
| ~A(){ |
| cout<<"A的析构函数调用"<<endl; |
| } |
| }; |
| |
| class B{ |
| public: |
| A a; |
| |
| B(){ |
| cout<<"B的构造函数调用"<<endl; |
| } |
| |
| ~B(){ |
| cout<<"B的析构函数调用"<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| B b2; |
| |
| return 0; |
| } |
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:
- 静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
示例1 :静态成员变量
| |
| |
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| static int x; |
| |
| private: |
| static int y; |
| }; |
| |
| int point::x=10; |
| |
| int point::y=20; |
| |
| int main(){ |
| |
| point p1; |
| |
| p1.x=20; |
| |
| cout<<p1.x<<endl; |
| |
| point p2; |
| |
| cout<<p2.x<<endl; |
| |
| cout<<point::x<<endl; |
| |
| |
| |
| return 0; |
| |
| } |
示例2:静态成员函数
| |
| |
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| static int x; |
| int y; |
| |
| static void show_pub(){ |
| cout<<x<<endl; |
| |
| } |
| |
| private: |
| static int z; |
| |
| |
| static void show_pri(){ |
| cout<<z<<endl; |
| } |
| }; |
| |
| int point::x=10; |
| int point::z=30; |
| |
| int main(){ |
| |
| point p1; |
| |
| p1.y=20; |
| |
| |
| p1.show_pub(); |
| |
| |
| point::show_pub(); |
| |
| |
| |
| return 0; |
| |
| } |
在C++
中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point1{ |
| |
| int x; |
| |
| }; |
| |
| class point2{ |
| |
| int x; |
| |
| static int y; |
| |
| void fx(){ |
| |
| } |
| |
| static void fy(){ |
| |
| } |
| }; |
| |
| int main(){ |
| |
| cout<<sizeof(point1)<<endl; |
| |
| cout<<sizeof(point2)<<endl; |
| |
| } |
注意: C++
编译器会给空对象分配一个字节,用于区分其存储空间
C++
中成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?
C++
通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
概念
- this指针是隐含每一个非静态成员函数内的一种指针
- this指针不需要定义,直接使用即可
用途:
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| |
| public: |
| |
| int x,y; |
| |
| |
| point(int x,int y){ |
| this->x=x; |
| this->y=y; |
| } |
| |
| point& add(point p){ |
| this->x+=p.x; |
| this->y+=p.y; |
| |
| return *this; |
| } |
| |
| }; |
| |
| void test01(){ |
| |
| point p1(1,2); |
| |
| cout<<p1.x<<" "<<p1.y<<endl; |
| |
| } |
| |
| void test02(){ |
| |
| point p2(1,1); |
| |
| point p3(0,0); |
| |
| p3.add(p2).add(p2).add(p2); |
| |
| cout<<p2.x<<" "<<p2.y<<endl; |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| test02(); |
| |
| return 0; |
| |
| } |
C++
中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(int a,int b):x(a),y(b) {} |
| |
| void show_1(){ |
| cout<<"YES"<<endl; |
| } |
| |
| void show_2(){ |
| |
| cout<<x<<" "<<y<<endl; |
| |
| |
| } |
| }; |
| |
| int main(){ |
| |
| point *p1=NULL; |
| |
| p1->show_1(); |
| |
| p1->show_2(); |
| |
| return 0; |
| |
| } |
常函数:
- 成员函数后加
const
后我们称为这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字
mutable
后,在常函数中依然可以修改
常对象:
- 声明对象前加
const
称该对象为常对象
- 常对象只能调用常函数
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x; |
| mutable int y; |
| |
| point(){ |
| x=10; |
| y=10; |
| } |
| |
| |
| |
| |
| void show_1() const{ |
| |
| |
| |
| |
| |
| |
| this->y=20; |
| cout<<x<<" "<<y<<endl; |
| } |
| |
| void show_2(){ |
| cout<<x<<" "<<y<<endl; |
| } |
| |
| |
| |
| |
| }; |
| |
| int main(){ |
| |
| point p1; |
| |
| p1.show_1(); |
| |
| const point p2; |
| |
| |
| p2.y=100; |
| |
| |
| p2.show_1(); |
| |
| |
| |
| return 0; |
| |
| } |
生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的三种实现
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| |
| friend void visit(point &p); |
| |
| public: |
| int x; |
| |
| point(){ |
| x=10; |
| y=10; |
| } |
| |
| private: |
| int y; |
| |
| }; |
| |
| void visit(point &p){ |
| cout<<p.x<<endl<<p.y<<endl; |
| } |
| |
| int main(){ |
| |
| point p1; |
| |
| visit(p1); |
| |
| } |
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| |
| friend class show; |
| |
| public: |
| int x; |
| |
| point(){ |
| x=10; |
| y=10; |
| } |
| |
| private: |
| int y; |
| |
| }; |
| |
| class show{ |
| |
| public: |
| |
| point p1; |
| |
| void visit(){ |
| cout<<p1.x<<" "<<p1.y<<endl; |
| } |
| |
| }; |
| |
| int main(){ |
| |
| show s1; |
| |
| s1.visit(); |
| |
| return 0; |
| |
| } |
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point; |
| class show |
| { |
| public: |
| show(); |
| void visit(); |
| |
| private: |
| point *p; |
| }; |
| |
| class point |
| { |
| |
| friend void show::visit(); |
| |
| public: |
| point(); |
| |
| public: |
| int x; |
| private: |
| int y; |
| }; |
| |
| point::point() |
| { |
| this->x=10; |
| this->y=10; |
| } |
| |
| show::show() |
| { |
| p = new point; |
| } |
| |
| void show::visit() |
| { |
| cout << "好基友正在访问" << p->x << endl; |
| cout << "好基友正在访问" << p->y << endl; |
| } |
| |
| void test01() |
| { |
| show s; |
| s.visit(); |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| return 0; |
| } |
运算符重载概念:利用operator
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
本质:
- 提供一个
operator 运算符()
函数,使得A operator 运算符(B)
的形式可以化简为A 运算符 B
的形式
作用:实现两个自定义数据类型相加的运算
示例1
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(){} |
| |
| point(int a,int b):x(a),y(b) {} |
| |
| point operator+(const point p){ |
| point t; |
| t.x=this->x+p.x; |
| t.y=this->y+p.y; |
| return t; |
| } |
| |
| point operator-(const point p){ |
| point t; |
| t.x=this->x-p.x; |
| t.y=this->y-p.y; |
| return t; |
| } |
| |
| }; |
| |
| int main(){ |
| |
| point p1(1,1); |
| |
| point p2(2,2); |
| |
| point p3=p2+p1; |
| |
| cout<<p3.x<<" "<<p3.y<<endl; |
| |
| p3=p2-p1; |
| |
| cout<<p3.x<<" "<<p3.y<<endl; |
| |
| return 0; |
| |
| } |
示例2
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(){} |
| |
| point(int a,int b):x(a),y(b){} |
| |
| }; |
| |
| point operator+(const point &p1,const point &p2){ |
| point t; |
| t.x=p1.x+p2.x; |
| t.y=p1.y+p2.y; |
| return t; |
| } |
| |
| point operator-(const point &p1,const point &p2){ |
| point t; |
| t.x=p1.x-p2.x; |
| t.y=p1.y-p2.y; |
| return t; |
| } |
| |
| int main(){ |
| |
| point p1(1,1); |
| |
| point p2(2,2); |
| |
| point p3=p1+p2; |
| |
| cout<<p3.x<<" "<<p3.y<<endl; |
| |
| p3=p2-p1; |
| |
| cout<<p3.x<<" "<<p3.y<<endl; |
| |
| return 0; |
| |
| } |
总结
- 对于内置的数据类型的表达式的的运算符是不可能改变的
- 不要滥用运算符重载
作用:可以输出自定义数据类型
注意 :一般使用全局函数实现
示例
| |
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(){} |
| |
| point(int a,int b):x(a),y(b){} |
| |
| }; |
| |
| |
| ostream& operator<<(ostream &cout,point &p){ |
| cout<<p.x<<" "<<p.y<<endl; |
| return cout; |
| } |
| |
| int main(){ |
| point p1(1,1); |
| |
| cout<<p1<<"链式输出"<<endl; |
| |
| return 0; |
| } |
作用: 通过重载递增运算符,实现自己的整型数据
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(){} |
| |
| point(int a,int b):x(a),y(b){} |
| |
| |
| point& operator++(){ |
| this->x++; |
| this->y++; |
| return *this; |
| } |
| |
| |
| point& operator++(int){ |
| this->x++; |
| this->y++; |
| return *this; |
| } |
| }; |
| |
| int main(){ |
| |
| point p1(1,1); |
| |
| p1++; |
| |
| ++p1; |
| |
| cout<<p1.x<<" "<<p1.y<<endl; |
| |
| return 0; |
| |
| } |
C++
编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符
operator=
, 对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int *x,*y; |
| |
| point(){} |
| |
| point(int a,int b){ |
| x=new int(a); |
| y=new int(b); |
| } |
| |
| |
| point& operator=(point &p){ |
| if(this->x!=NULL){ |
| delete this->x; |
| this->x=NULL; |
| } |
| if(this->y!=NULL){ |
| delete this->y; |
| this->y=NULL; |
| } |
| |
| |
| |
| this->x=new int(*p.x); |
| this->y=new int(*p.y); |
| |
| return *this; |
| } |
| |
| ~point(){ |
| if(this->x!=NULL){ |
| delete this->x; |
| this->x=NULL; |
| } |
| if(this->y!=NULL){ |
| delete this->y; |
| this->y=NULL; |
| } |
| } |
| }; |
| |
| int main(){ |
| |
| point p1(1,1); |
| |
| point p2(2,2); |
| |
| p1=p2; |
| |
| cout<<*p1.x<<" "<<*p1.y<<endl; |
| |
| return 0; |
| |
| } |
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(int a,int b):x(a),y(b){} |
| |
| bool operator==(const point &p){ |
| if(this->x==p.x&&this->y==p.y) return 1; |
| else return 0; |
| } |
| }; |
| |
| int main(){ |
| |
| point p1(1,1); |
| |
| point p2(2,2); |
| |
| if(p1==p2) cout<<"YES"<<endl; |
| else cout<<"NO"<<endl; |
| |
| return 0; |
| |
| } |
特点
- 函数调用运算符 () 也可以重载
- 由于重载后使用的方式非常像函数的调用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class print{ |
| public: |
| void operator()(auto s){ |
| cout<<s<<endl; |
| } |
| }; |
| |
| class add{ |
| public: |
| int operator()(int a,int b){ |
| return a+b; |
| } |
| }; |
| |
| void test01(){ |
| |
| print p; |
| |
| p("lys is dog"); |
| |
| } |
| |
| void test02(){ |
| |
| add a; |
| |
| cout<<a(1,2)<<endl; |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| test02(); |
| |
| return 0; |
| |
| } |
继承是面向对象三大特性之一
我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。
这个时候我们就可以考虑利用继承的技术,减少重复代码
定义和概念
继承是类的重要特性。A类继承B类,我们称B类为“基类”,A为“派生类”。A类继承了B类之后,A类就具有了B类的部分成员,具体得到了那些成员,这得由两个方面决定:
基本语法:class A : public B
A
类称为派生类 或 派生类
B
类称为基类 或 基类
示例:对于一个人来说,有姓名,年龄,性别,这些基本特征,而像是职位之类的特征则是因人而异的特征,在创建人的类的时候,我们可以通过继承的技术,减少对基本特征的定义等操作的代码。
普通实现:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| |
| class student{ |
| public: |
| |
| string name,sex; |
| int year; |
| |
| student(string n,int x,string s):name(n),year(x),sex(s){} |
| |
| void show_name(){ |
| cout<<"名字:"<<name<<endl; |
| } |
| |
| void show_year(){ |
| cout<<"年龄:"<<year<<endl; |
| } |
| |
| void show_sex(){ |
| if(sex=="boy"){ |
| cout<<"性别:男"<<endl; |
| } |
| else cout<<"性别:女"<<endl; |
| } |
| |
| void show_position(){ |
| cout<<"是一个学生"<<endl; |
| } |
| |
| }; |
| |
| |
| class parent{ |
| public: |
| |
| string name,sex; |
| int year; |
| |
| parent(string n,int x,string s):name(n),year(x),sex(s){} |
| |
| void show_name(){ |
| cout<<"名字:"<<name<<endl; |
| } |
| |
| void show_year(){ |
| cout<<"年龄:"<<year<<endl; |
| } |
| |
| void show_sex(){ |
| if(sex=="boy"){ |
| cout<<"性别:男"<<endl; |
| } |
| else cout<<"性别:女"<<endl; |
| } |
| |
| void show_position(){ |
| cout<<"是一名家长"<<endl; |
| } |
| }; |
| |
| |
| class teacher{ |
| public: |
| |
| string name,sex; |
| int year; |
| |
| teacher(string n,int x,string s):name(n),year(x),sex(s){} |
| |
| void show_name(){ |
| cout<<"名字:"<<name<<endl; |
| } |
| |
| void show_year(){ |
| cout<<"年龄:"<<year<<endl; |
| } |
| |
| void show_sex(){ |
| if(sex=="boy"){ |
| cout<<"性别:男"<<endl; |
| } |
| else cout<<"性别:女"<<endl; |
| } |
| |
| void show_position(){ |
| cout<<"是一位老师"<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| |
| student s1("lys",20,"boy"); |
| s1.show_name(); |
| s1.show_year(); |
| s1.show_position(); |
| cout<<endl; |
| |
| |
| parent p1("mama",40,"girl"); |
| p1.show_name(); |
| p1.show_year(); |
| p1.show_position(); |
| cout<<endl; |
| |
| |
| teacher t1("yxc",30,"boy"); |
| t1.show_name(); |
| t1.show_year(); |
| t1.show_position(); |
| cout<<endl; |
| |
| return 0; |
| |
| } |
继承实现:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| |
| class person{ |
| public: |
| string name,sex; |
| int year; |
| |
| person(string n,int x,string s):name(n),year(x),sex(s){} |
| |
| void show_name(){ |
| cout<<"名字:"<<name<<endl; |
| } |
| |
| void show_year(){ |
| cout<<"年龄:"<<year<<endl; |
| } |
| |
| void show_sex(){ |
| if(sex=="boy"){ |
| cout<<"性别:男"<<endl; |
| } |
| else cout<<"性别:女"<<endl; |
| } |
| }; |
| |
| |
| class student : public person{ |
| public: |
| |
| student(string n,int x,string s):person(n,x,s){} |
| |
| void show_position(){ |
| cout<<"是一个学生"<<endl; |
| } |
| |
| }; |
| |
| |
| class parent : public person{ |
| public: |
| |
| parent(string n,int x,string s):person(n,x,s){} |
| |
| void show_position(){ |
| cout<<"是一名家长"<<endl; |
| } |
| }; |
| |
| |
| class teacher : public person{ |
| public: |
| |
| teacher(string n,int x,string s):person(n,x,s){} |
| |
| void show_position(){ |
| cout<<"是一位老师"<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| |
| student s1("lys",20,"boy"); |
| s1.show_name(); |
| s1.show_year(); |
| s1.show_position(); |
| cout<<endl; |
| |
| |
| parent p1("mama",40,"girl"); |
| p1.show_name(); |
| p1.show_year(); |
| p1.show_position(); |
| cout<<endl; |
| |
| |
| teacher t1("yxc",30,"boy"); |
| t1.show_name(); |
| t1.show_year(); |
| t1.show_position(); |
| cout<<endl; |
| |
| return 0; |
| |
| } |
总结:
-
继承的好处:可以减少重复的代码
-
派生类中的成员,包含两大部分:
- 一类是从基类继承过来的(基本特征)
- 一类是自己增加的成员(因人而异的特征)。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。
继承的语法:class 派生类 : 继承方式 基类
继承方式一共有三种:
示例1 公共继承:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x; |
| |
| protected: |
| int y; |
| |
| private: |
| int z; |
| }; |
| |
| |
| class point_Pub:public point{ |
| public: |
| |
| |
| point_Pub(){ |
| x=10; |
| y=20; |
| |
| } |
| |
| void show(){ |
| cout<<"x = "<<x<<endl; |
| cout<<"y = "<<y<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| point_Pub p1; |
| |
| p1.show(); |
| |
| |
| cout<<"p1.x = "<<p1.x<<endl; |
| |
| |
| |
| return 0; |
| |
| } |
示例2 保护继承
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x; |
| |
| protected: |
| int y; |
| |
| private: |
| int z; |
| }; |
| |
| |
| class point_Pro:protected point{ |
| public: |
| |
| |
| point_Pro(){ |
| x=10; |
| y=20; |
| |
| } |
| |
| void show(){ |
| cout<<"x = "<<x<<endl; |
| cout<<"y = "<<y<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| point_Pro p1; |
| |
| p1.show(); |
| |
| |
| |
| |
| |
| |
| return 0; |
| |
| } |
示例3 私有继承
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x; |
| |
| protected: |
| int y; |
| |
| private: |
| int z; |
| }; |
| |
| |
| class point_Pri:private point{ |
| public: |
| |
| |
| point_Pri(){ |
| x=10; |
| y=20; |
| |
| } |
| |
| void show(){ |
| cout<<"x = "<<x<<endl; |
| cout<<"y = "<<y<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| point_Pri p1; |
| |
| p1.show(); |
| |
| |
| |
| |
| |
| |
| return 0; |
| |
| } |
问题:从基类继承过来的成员,哪些属于派生类对象中?
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| |
| public: |
| int x; |
| |
| protected: |
| int y; |
| |
| private: |
| int z; |
| |
| static int l; |
| }; |
| |
| class point_son:public point{ |
| public: |
| int m; |
| }; |
| |
| int main(){ |
| |
| cout<<sizeof(point_son)<<endl; |
| |
| |
| } |
结论: 基类中私有成员也是被派生类继承下去了,只是由编译器给隐藏后访问不到
特点
- 派生类对象可以赋值给基类的对象/基类的指针/基类的引用
- 基类的指针可以通过强制类型转换赋值给派生类的指针。 但是必须是基类的指针是指向派生类对象时才是安全的
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| point(int a,int b):x(a),y(b){} |
| }; |
| |
| class point_son:public point{ |
| public: |
| int s_x=10; |
| |
| point_son(int a,int b):point(a,b){} |
| }; |
| |
| void test01(){ |
| |
| point_son s1(1,1); |
| |
| point p1=s1; |
| cout<<p1.x<<" "<<p1.y<<endl; |
| |
| point *p2=&s1; |
| cout<<p2->x<<" "<<p2->y<<endl; |
| |
| point &p3=s1; |
| cout<<p3.x<<" "<<p3.y<<endl; |
| |
| } |
| |
| void test02(){ |
| |
| point p1(1,1); |
| |
| |
| |
| |
| |
| point *p2=&p1; |
| point_son *s2=(point_son*)p2; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| test02(); |
| |
| return 0; |
| |
| } |
派生类继承基类后,当创建派生类对象,也会调用基类的构造函数
问题:基类和派生类的构造和析构顺序是谁先谁后?
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| |
| public: |
| int x; |
| |
| point(int a):x(a){ |
| cout<<"基类的构造函数调用"<<endl; |
| } |
| |
| ~point(){ |
| cout<<"基类的析构函数调用"<<endl; |
| } |
| }; |
| |
| class point_son:public point{ |
| public: |
| point_son(int a):point(a){ |
| cout<<"派生类的构造函数调用"<<endl; |
| } |
| |
| ~point_son(){ |
| cout<<"派生类的析构函数调用"<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| point_son p1(10); |
| |
| |
| return 0; |
| |
| } |
总结:继承中 先调用基类构造函数,再调用派生类构造函数,析构顺序与构造相反
问题:当派生类与基类出现同名的成员,如何通过派生类对象,访问到派生类或基类中同名的数据呢?
- 访问派生类同名成员 直接访问即可
- 访问基类同名成员 需要加作用域
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| |
| public: |
| int x=20; |
| |
| void show(){ |
| cout<<"基类void show()的函数调用"<<endl; |
| } |
| |
| }; |
| |
| class point_son:public point{ |
| public: |
| |
| int x=10; |
| |
| |
| void show(){ |
| cout<<"子类void show()的函数调用"<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| point_son s1; |
| |
| cout<<"子类point_son下的x = "<<s1.x<<endl; |
| cout<<"基类point下的x = "<<s1.point::x<<endl; |
| |
| s1.show(); |
| s1.point::show(); |
| |
| return 0; |
| |
| } |
总结:
- 派生类对象可以直接访问到派生类中同名成员
- 派生类对象加作用域可以访问到基类同名成员
- 当派生类与基类拥有同名的成员函数,派生类会隐藏基类中同名成员函数,加作用域可以访问到基类中同名函数
问题:继承中同名的静态成员在派生类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一致
- 访问派生类同名成员 直接访问即可
- 访问基类同名成员 需要加作用域
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| static int x; |
| static void show(){ |
| cout<<"基类静态成员函数的调用"<<endl; |
| } |
| }; |
| |
| class point_son:public point{ |
| public: |
| static int x; |
| static void show(){ |
| cout<<"派生类静态成员函数的调用"<<endl; |
| } |
| }; |
| |
| int point::x=20; |
| int point_son::x=10; |
| |
| |
| void test01(){ |
| |
| cout<<"通过对象访问"<<endl; |
| |
| point_son p1; |
| |
| cout<<"子类point_son下的x = "<<p1.x<<endl; |
| cout<<"基类point下的x = "<<p1.point::x<<endl; |
| |
| p1.show(); |
| p1.point_son::show(); |
| |
| } |
| |
| |
| void test02(){ |
| |
| cout<<"通过类名访问"<<endl; |
| |
| cout<<"子类point_son下的x = "<<point::x<<endl; |
| cout<<"基类point下的x = "<<point_son::x<<endl; |
| |
| point::show(); |
| point_son::show(); |
| point_son::point::show(); |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| cout<<endl; |
| |
| test02(); |
| |
| return 0; |
| |
| } |
总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)
C++
允许一个类继承多个类
语法: class 派生类 :继承方式 基类1 , 继承方式 基类2...
注意:多继承可能会引发基类中有同名成员出现,需要加作用域区分
C++
实际开发中不建议用多继承
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point_1{ |
| public: |
| int x=10; |
| |
| point_1(){ |
| cout<<"point_1的构造函数调用"<<endl; |
| } |
| |
| ~point_1(){ |
| cout<<"point_1的析构函数调用"<<endl; |
| } |
| }; |
| |
| class point_2{ |
| public: |
| int x=20; |
| |
| point_2(){ |
| cout<<"point_2的构造函数调用"<<endl; |
| } |
| |
| ~point_2(){ |
| cout<<"point_2的析构函数调用"<<endl; |
| } |
| }; |
| |
| |
| class point_son:public point_1,public point_2{ |
| public: |
| int x=30; |
| |
| point_son(){ |
| cout<<"point_son的构造函数调用"<<endl; |
| } |
| |
| ~point_son(){ |
| cout<<"point_son的析构函数调用"<<endl; |
| } |
| }; |
| |
| int main(){ |
| |
| |
| |
| point_son s1; |
| |
| cout<<"point_1下的x = "<<s1.point_1::x<<endl; |
| |
| cout<<"point_2下的x = "<<s1.point_2::x<<endl; |
| |
| cout<<"point_son下的x = "<<s1.x<<endl; |
| |
| return 0; |
| |
| } |
总结: 多继承中如果基类中出现了同名情况,派生类使用时候要加作用域
菱形继承概念:
- 两个派生类继承同一个基类
- 又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
典型的菱形继承案例:
- 先创建一个
person
类作为基类
- 再创建两个
person
的派生类father
类和mother
类
- 最后创建一个
son
类同时继承father
类和mother
类
菱形继承问题:
-
father
继承了person
的数据,mother
同样继承了person
的数据,当son
使用数据时,就会产生二义性。
-
son
继承自person
的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| int year; |
| string sex; |
| }; |
| |
| class father:public person{ |
| public: |
| string name; |
| }; |
| |
| class mother:public person{ |
| public: |
| string name; |
| }; |
| |
| class son:public father,public mother{ |
| public: |
| string name; |
| }; |
| |
| int main(){ |
| |
| son s1; |
| |
| s1.father::sex="男"; |
| s1.father::year=40; |
| s1.father::name="baba"; |
| |
| s1.mother::sex="女"; |
| s1.mother::year=38; |
| s1.mother::name="mama"; |
| |
| |
| |
| |
| s1.son::name="lys"; |
| |
| |
| |
| cout<<"father: "<<s1.father::name<<endl<<"性别: "<<s1.father::sex<<endl<<"年龄: "<<s1.father::year<<endl; |
| cout<<endl; |
| |
| cout<<"mother: "<<s1.mother::name<<endl<<"性别: "<<s1.mother::sex<<endl<<"年龄: "<<s1.mother::year<<endl; |
| cout<<endl; |
| |
| |
| cout<<"son: "<<s1.name<<endl; |
| cout<<endl; |
| |
| return 0; |
| |
| } |
解决:以上菱形继承带来的问题可以使用虚继承的技术来解决
关键字:virtual
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| int year; |
| string sex; |
| }; |
| |
| |
| |
| class father:virtual public person{ |
| public: |
| string name; |
| }; |
| |
| class mother:virtual public person{ |
| public: |
| string name; |
| }; |
| |
| class son:public father,public mother{ |
| public: |
| string name; |
| }; |
| |
| int main(){ |
| |
| son s1; |
| |
| s1.father::sex="男"; |
| s1.father::year=40; |
| s1.father::name="baba"; |
| |
| s1.mother::sex="女"; |
| s1.mother::year=38; |
| s1.mother::name="mama"; |
| |
| |
| s1.son::year=20; |
| s1.son::sex="Dog"; |
| s1.son::name="lys"; |
| |
| |
| cout<<"father: "<<s1.father::name<<endl<<"性别: "<<s1.father::sex<<endl<<"年龄: "<<s1.father::year<<endl; |
| cout<<endl; |
| |
| cout<<"mother: "<<s1.mother::name<<endl<<"性别: "<<s1.mother::sex<<endl<<"年龄: "<<s1.mother::year<<endl; |
| cout<<endl; |
| |
| cout<<"son: "<<s1.name<<endl<<"性别: "<<s1.sex<<endl<<"年龄: "<<s1.year<<endl; |
| cout<<endl; |
| |
| return 0; |
| |
| } |
总结:
- 菱形继承带来的主要问题是派生类继承两份相同的数据,导致资源浪费以及毫无意义
- 利用虚继承可以解决菱形继承问题
多态是C++
面向对象三大特性之一
多态分为两类
- 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
- 动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
多态满足条件:
多态使用条件
重写:函数返回值类型 函数名 参数列表 完全一致称为重写
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| void show(){ |
| cout<<"是一个人"<<endl; |
| } |
| }; |
| |
| class male:public person{ |
| public: |
| void show(){ |
| cout<<"是一个男人"<<endl; |
| } |
| }; |
| |
| class female:public person{ |
| public: |
| void show(){ |
| cout<<"是一个女人"<<endl; |
| } |
| }; |
| |
| |
| void show_sex(person &p){ |
| |
| p.show(); |
| |
| } |
| |
| int main(){ |
| |
| male m1; |
| |
| show_sex(m1); |
| |
| female f1; |
| |
| show_sex(f1); |
| |
| return 0; |
| |
| } |
虚函数实现
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| |
| virtual void show(){ |
| cout<<"是一个人"<<endl; |
| } |
| }; |
| |
| class male:public person{ |
| public: |
| virtual void show(){ |
| cout<<"是一个男人"<<endl; |
| } |
| }; |
| |
| class female:public person{ |
| public: |
| void show(){ |
| cout<<"是一个女人"<<endl; |
| } |
| }; |
| |
| |
| void show_sex(person &p){ |
| |
| p.show(); |
| |
| } |
| |
| int main(){ |
| |
| |
| |
| |
| male m1; |
| |
| show_sex(m1); |
| |
| female f1; |
| |
| show_sex(f1); |
| |
| return 0; |
| |
| } |
多态的优点:
- 代码组织结构清晰
- 可读性强
- 利于前期和后期的扩展以及维护
示例
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| |
| |
| class base{ |
| public: |
| int x,y; |
| |
| virtual int calculate(){ |
| return 0; |
| } |
| }; |
| |
| |
| class add:public base{ |
| public: |
| int calculate(){ |
| return x+y; |
| } |
| }; |
| |
| |
| class sub:public base{ |
| public: |
| int calculate(){ |
| return x-y; |
| } |
| }; |
| |
| |
| class mul:public base{ |
| public: |
| int calculate(){ |
| return x*y; |
| } |
| }; |
| |
| int main(){ |
| |
| |
| add a; |
| base *b1=&a; |
| b1->x=10; |
| b1->y=20; |
| cout<<b1->calculate()<<endl; |
| |
| |
| sub s; |
| base &b2=s; |
| b2.x=10; |
| b2.y=20; |
| cout<<b2.calculate()<<endl; |
| |
| |
| base *b3=new mul; |
| b3->x=10; |
| b3->y=20; |
| cout<<b3->calculate()<<endl; |
| delete b3; |
| |
| return 0; |
| |
| } |
总结:C++
开发提倡利用多态设计程序架构,因为多态优点很多
在多态中,通常基类中虚函数的实现是毫无意义的,主要都是调用派生类重写的内容,可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类(只要有一个函数是纯虚函数,就是抽象类)
抽象类特点:
- 无法实例化对象
- 派生类必须重写抽象类中的纯虚函数,否则也属于抽象类
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class point{ |
| public: |
| int x,y; |
| |
| |
| |
| virtual void show()=0; |
| }; |
| |
| class point_son_1:public point{ |
| public: |
| |
| }; |
| |
| class point_son_2:public point{ |
| public: |
| void show(){ |
| cout<<x<<" "<<y<<endl; |
| } |
| }; |
| |
| void test01(){ |
| |
| |
| |
| |
| } |
| |
| void test02(){ |
| |
| point_son_2 s2; |
| s2.x=10; |
| s2.y=20; |
| s2.show(); |
| |
| point &p2=s2; |
| p2.show(); |
| |
| point *p3=new point_son_2; |
| p3->x=10; |
| p3->y=20; |
| p3->show(); |
| |
| } |
| |
| int main(){ |
| |
| test01(); |
| |
| test02(); |
| |
| return 0; |
| |
| } |
多态使用时,如果派生类中有属性开辟到堆区,那么基类指针在释放时无法调用到派生类的析构代码
解决方式:将基类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
- 可以解决基类指针释放派生类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| person(){ |
| cout<<"person的构造函数调用"<<endl; |
| } |
| |
| virtual void show()=0; |
| |
| ~person(){ |
| cout<<"person的析构函数调用"<<endl; |
| } |
| }; |
| |
| class student:public person{ |
| public: |
| string *name; |
| |
| student(string s){ |
| cout<<"student的构造函数调用"<<endl; |
| name=new string(s); |
| } |
| |
| void show(){ |
| cout<<*name<<" is dog "<<endl; |
| } |
| |
| ~student(){ |
| cout<<"student的析构函数调用"<<endl; |
| if(name!=NULL){ |
| delete name; |
| name=NULL; |
| } |
| } |
| }; |
| |
| int main(){ |
| |
| person *p=new student("lys"); |
| p->show(); |
| |
| |
| delete p; |
| |
| return 0; |
| |
| } |
解决方法1 将基类函数的析构函数改为虚析构
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| person(){ |
| cout<<"person的构造函数调用"<<endl; |
| } |
| |
| virtual void show()=0; |
| |
| |
| virtual ~person(){ |
| cout<<"person的虚析构函数调用"<<endl; |
| } |
| }; |
| |
| class student:public person{ |
| public: |
| string *name; |
| |
| student(string s){ |
| cout<<"student的构造函数调用"<<endl; |
| name=new string(s); |
| } |
| |
| void show(){ |
| cout<<*name<<" is dog "<<endl; |
| } |
| |
| ~student(){ |
| cout<<"student的析构函数调用"<<endl; |
| if(name!=NULL){ |
| delete name; |
| name=NULL; |
| } |
| } |
| }; |
| |
| int main(){ |
| |
| person *p=new student("lys"); |
| p->show(); |
| |
| delete p; |
| |
| return 0; |
| |
| } |
解决方法2 利用纯虚析构函数的方法
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| using namespace std; |
| |
| class person{ |
| public: |
| person(){ |
| cout<<"person的构造函数调用"<<endl; |
| } |
| |
| virtual void show()=0; |
| |
| |
| virtual ~person()=0; |
| }; |
| |
| |
| person::~person(){ |
| cout<<"person的纯虚析构函数调用"<<endl; |
| } |
| |
| class student:public person{ |
| public: |
| string *name; |
| |
| student(string s){ |
| cout<<"student的构造函数调用"<<endl; |
| name=new string(s); |
| } |
| |
| void show(){ |
| cout<<*name<<" is dog "<<endl; |
| } |
| |
| ~student(){ |
| cout<<"student的析构函数调用"<<endl; |
| if(name!=NULL){ |
| delete name; |
| name=NULL; |
| } |
| } |
| }; |
| |
| int main(){ |
| |
| person *p=new student("lys"); |
| p->show(); |
| |
| delete p; |
| |
| return 0; |
| |
| } |
总结:
1. 虚析构或纯虚析构就是用来解决通过基类指针释放派生类对象
2. 如果派生类中没有堆区数据,可以不写为虚析构或纯虚析构
3. 拥有纯虚析构函数的类也属于抽象类
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++
中对文件操作需要包含头文件 <fstream>
文件类型分为两种:
- 文本文件 - 文件以文本的ASCII码形式存储在计算机中
- 二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
步骤:
-
包含头文件
#include <fstream>
-
创建流对象
ofstream ofs;
-
打开文件
ofs.open("文件路径",打开方式);
-
写数据
ofs << "写入的数据";
-
关闭文件
ofs.close();
文件打开方式:
打开方式 |
解释 |
ios::in |
为读文件而打开文件 |
ios::out |
为写文件而打开文件 |
ios::ate |
初始位置:文件尾 |
ios::app |
追加方式写文件 |
ios::trunc |
如果文件存在先删除,再创建 |
ios::binary |
二进制方式 |
注意: 文件打开方式可以配合使用,利用|操作符
例如:用二进制方式写文件 ios::binary | ios:: out
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| #include <fstream> //包含头文件 |
| using namespace std; |
| |
| int main(){ |
| |
| ofstream o1; |
| |
| o1.open("test.txt",ios::out); |
| |
| |
| o1<<"lys"<<endl; |
| o1<<"ege 20"<<endl; |
| o1<<"is a dog"<<endl; |
| |
| o1.close(); |
| |
| return 0; |
| |
| } |
总结:
- 文件操作必须包含头文件
fstream
- 读文件可以利用
ofstream
,或者fstream
类
- 打开文件时候需要指定操作文件的路径,以及打开方式
- 利用
<<
可以向文件中写数据
- 操作完毕,要关闭文件
读文件步骤如下:
-
包含头文件
#include <fstream>
-
创建流对象
ifstream ifs;
-
打开文件并判断文件是否打开成功
ifs.open("文件路径",打开方式);
-
读数据
四种方式读取
-
关闭文件
ifs.close();
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| #include <fstream> //包含头文件 |
| using namespace std; |
| |
| int main(){ |
| |
| ifstream i1; |
| |
| i1.open("test.txt",ios::in); |
| |
| if(!i1.is_open()){ |
| cout<<"找不到该文件"<<endl; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| string s4; |
| while(getline(i1,s4)){ |
| cout<<s4<<endl; |
| } |
| |
| i1.close(); |
| |
| return 0; |
| |
| } |
总结:
- 读文件可以利用
ifstream
,或者fstream
类
- 利用
is_open
函数可以判断文件是否打开成功
close
关闭文件
以二进制的方式对文件进行读写操作
打开方式要指定为ios::binary
二进制方式写文件主要利用流对象调用成员函数write
函数原型 :ostream& write(const char * buffer,int len);
参数解释:字符指针buffer
指向内存中一段存储空间。len
是读写的字节数
示例:
| #include <stdio.h> |
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| #include <fstream> //包含头文件 |
| using namespace std; |
| |
| int main(){ |
| |
| ofstream o1; |
| |
| o1.open("test_01.txt",ios::out|ios::binary); |
| |
| |
| char s[1024]="lys is a dog"; |
| o1.write((const char*)&s,sizeof(s)); |
| |
| o1.close(); |
| |
| return 0; |
| |
| } |
总结:
- 文件输出流对象 可以通过
write
函数,以二进制方式写数据
- 不要用读入
string
类型
- 原因:
string
在stl
中其实是一个类,这样写入的其实是test_01
这个类对象,因此写到文件的其实是这个类的数据和指向这个类的指针。同时,因为string
类的字符串是用new
在堆上分配的,string
类本身只包含字符串的指针,用c_str()
这个成员函数可以获得这个指针
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer,int len);
参数解释:字符指针buffer
指向内存中一段存储空间。len
是读写的字节数
示例:
| #include <iostream> |
| #include <string> |
| #include <algorithm> |
| #include <fstream> //包含头文件 |
| using namespace std; |
| |
| int main(){ |
| |
| ifstream i1; |
| |
| i1.open("test_01.txt",ios::out|ios::binary); |
| |
| if(!i1.is_open()){ |
| cout<<"找不到该文件"<<endl; |
| } |
| |
| |
| char s[1024]; |
| i1.read((char*)&s,sizeof(s)); |
| cout<<s<<endl; |
| |
| i1.close(); |
| |
| return 0; |
| |
| } |
总结
- 文件输入流对象 可以通过read函数,以二进制方式读数据
评论