黑马程序员C++核心编程学习笔记
黑马程序员C++核心编程学习笔记
一、内存
1.1 内存四区
C++程序在执行时,将内存大致分为4个区域:代码区,全局区,栈区,堆区
-
代码区:存放函数体的的二进制代码,操作系统管理。
🔵特点:①共享 :对于频繁被执行的程序,只需要在内存中有一份代码即可。
②只读:目的是防止程序意外地修改了它的指令。 -
全局区:存放全局变量、静态变量和常量(除了const修饰的局部变量)。程序结束时由操作系统释放。
//全局变量
int g_a = 10;
const int c_g_a = 10;
int main()
{int l_a = 10;//静态变量static int s_a = 10;//常量:字符串常量/const修饰的变量(全局/局部变量)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
-
栈区:存放函数的参数值、局部变量。由编译器自动分配和释放。
🔴注意:不要返回局部变量的地址。
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
),若程序员不释放,程序结束时由操作系统回收。
int* funcation()
{//new关键字,可以将数据开辟到堆区上//指针本质上也是局部变量,存放在栈区上,但是保存的数据在堆区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操作符
//new的基本用法
int* p = new int(10);//在堆区创建整型变量,返回该变量的地址
delete p;//释放
int* parr = new int[10];//在堆区创建一个元素为10的整型数组,返回数组首元素的地址
delete[] arr;//释放一个数组
12345
测试delete
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 引用基本使用和注意事项
作用:给变量起个别名。
语法:数据类型
&
别名
=原名
本质:指针常量
//eg.
int a = 10;
int& b = a;
123
🔴注意:
①引用必须初始化。
②引用一旦初始化就不可以更改了(如下的例子相当于int *const b=&a
)
//①引用必须初始化int a = 10;int& b;//错了!!!//②引用一旦初始化就不可以更改了int a = 10;int c = 20;int& b = a;int& b = c; //错了!!!
123456789
2.2 引用做函数参数
//eg.
/*实现数值交换*/
//1.传址交换
void swap1(int* a, int* b)
{int temp = *a;*a = *b;*b = temp;
}
//2.引用
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 引用函数返回值
🔴注意:
①不要返回局部变量的引用。函数返回时,局部变量会被释放,引用或指针指向的内容会失效
②函数的调用可以作为左值。
//eg.
//①不要返回局部变量的引用
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 常量引用
目的:用来修饰形参,防止误操作。
int &ret=10; //错了!,引用本身需要一个合法的内存空间。
1
/*
相当于编译器先创建一个临时变量:int temp=10;
然后进行起别名:int& ret=temp;
*/
const int& ret = 10;
12345
🔴注意:用常量引用之后不可以更改数据。
//eg.
int main()
{const int& ret = 10;ret = 100;//errcout << "ret=" << ret << endl;return 0;
}
12345678
C++推荐引用,因为语法方便,编译器帮我们做了指针的内容。
三、函数提高
3.1 函数默认参数
语法:返回类型
函数名
(参数 =默认值)
{}
用法:如果自己传入数据就用自己的,如果没有就用默认值。
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
🔴注意:
①默认值必须放在右边。
int test(int a, int b = 10, int c);//err
1
②声明和实现,有且只能有一个有默认参数。否则可能出现二义。
3.2 函数占位参数
语法:返回类型
函数名
(数据类型)
{}
//eg.
void test1(int a, int)
{cout << "haha" << endl;}
void test2(int a, int b=10)//占位参数可以有默认参数
{cout << "haha" << endl;}
int main()
{test1(1);//errtest1(1, 1);test2(1);test2(1, 1);return 0;
}
12345678910111213141516171819
3.3 函数重载
作用:函数名可以相同,提高复用率。
满足条件:
①同一个作用域。
②函数名称相同。
③参数类型不同/个数不同/顺序不同。
//eg.
//在全局作用域
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
🔴注意:
①函数的返回值不可以作为函数重载的条件!出现二义。
//eg.
void test(int a)
{cout << "调用test(int a)" << endl;
}
int test(int a)
{cout << "调用test(int a)" << endl;
}
123456789
②引用作为重载条件
//eg.
//引用作为重载条件
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
③函数重载碰到默认参数
//eg.
//函数重载碰到默认参数
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);//err,出现二义return 0;
}
123456789101112131415
四、类与对象
C++面向对象三大特性:封装、继承、多态
4.1 封装
🟦意义:
①将属性和行为作为一个整体。(放在一个class
里面)
②将属性和行为加以权限控制。
public
公共权限:类内外都可以访问
protected
保护权限: 类外不可以访问
private
私有权限: 类外不可以访问
//eg.1定义一个圆类
#define PI 3.14
//class 定义一个类 circle是类的名字
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
//eg.2设计一个学生类
class student
{
public://属性string name;//string 类处理起字符串来会方便很多,完全可以代替C语言中的字符数组或字符串指针。int id;//行为void show() {cout << "姓名:" << name << "学号:" << id << endl;}
};
int main()
{student s1;s1.name = "xiyang";s1.id = 1;s1.show();return 0;
}
1234567891011121314151617181920
//eg.3 公共权限,私有权限,保护权限访问的例子
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"; //errp1.password = 456;//errreturn 0;
}
12345678910111213141516171819202122232425
🔴注意:
struct
与class
的区别:
struct
默认权限为:共有
class
默认权限为:私有
4.1.1 成员属性设置为私有
🟦意义:
①可以直接控制读写的权限。
②对于写可以检测数据的有效性。
//eg.
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.分别用全局函数和成员函数判断两个立方体是否相等
*/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;elsereturn false;}};
//全局函数判断
bool isSame(cube& c1, cube& c2)
{if (c1.GetL() == c2.GetL() && c1.GetW() == c2.GetW() && c1.GetH() == c2.GetH())return true;elsereturn 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;}elsecout << "全局函数判断c1 c2不相等" << endl;bool ret2 = c1.isSameByClass(c2);if (ret2){cout << "成员函数判断c1 c2相等" << endl;}elsecout << "成员函数判断c1 c2不相等" << endl;return 0;
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
设计案例2:点和圆的关系
/*要求:1.设计一个圆形类和一个点类2.计算点和圆的关系
*///由简单的数学可知:一个点(x,y)和圆的(x,y,r)的关系有三种:
//1.点在圆内:点到圆心的距离d < r
//2.点在圆上:d=r
//3.点在圆外:d>r
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;elsereturn -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 构造函数
定义:主要作用在创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
语法:类名
()
{ }
分类:按参数分:有参,无参;按类型分:普通构造,拷贝构造。
调用方式:括号法,显示法,隐式转换法.
🔴注意:
①构造函数可以有参数,因此可以重载。
②程序在调用对象时会自动调用构造,无须手动调用,且只用调用一次。
//构造的分类和调用
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); //p2的年龄初始化为10person p3(p2); //显示法person p4 = person();person p5 = person(10);person p6 = person(p5);//person()为匿名对象,没有名字,但创建了对象//不要用拷贝构造函数初始化匿名对象,如person(p3),等价于person p3//隐式转换法person p7 = 10;//转换为:person p7=person(10)
}
12345678910111213141516171819202122232425262728293031323334353637383940
构造函数的调用规则:
创建一个类,C++至少给每一个类添加4个函数:默认构造(空实现),析构函数(空实现),拷贝构造(值拷贝),赋值运算符Operator=对属性进行值拷贝(4.5.4中介绍)
①如果用户定义一个有参构造函数,C++不会提供默认构造函数,但是会提供拷贝构造函数。
②如果用户定义一个拷贝构造函数,C++不会提供别的构造函数。
//eg.①如果用户定义一个有参构造函数,C++不会提供默认构造函数,但是会提供拷贝构造函数
class person
{
public:person(int a){age = a;cout << "默认构造函数的调用" << endl;}int show(){return age;}
private:int age;
};
int main()
{person p1;//errperson p2(18);person p3(p2);//拷贝构造函数cout << "p2的年龄为:" << p2.show() << endl;return 0;
}
123456789101112131415161718192021222324
//②如果用户定义一个拷贝构造函数,C++不会提供别的构造函数
class person
{
public:person(const person& p){age = p.age;//克隆数据cout << "拷贝构造函数的调用" << endl;}int show(){return age;}
private:int age;
};
int main()
{person p1;//errperson p2(18);//errperson p3(p1);cout << "p2的年龄为:" << p2.show() << endl;return 0;
}
123456789101112131415161718192021222324
4.3.2 析构函数
定义:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
语法:~类名
()
{ }
🔴注意:
①析构函数不可以有参数,因此不可以重载。
②程序在对象销毁前会自动调用析构,无须手动调用,且只用调用一次。
//eg. 构造函数和析构函数例子
class person
{
public:person(){cout << "构造函数的调用" << endl;}~person(){cout << "析构函数的调用" << endl;}
};
int main()
{person p;//创建在栈上,在对象销毁前自动调用析构函数return 0;
}
123456789101112131415161718
4.3.2.1 拷贝函数调用的时机
C++中拷贝函数调用一般有三种情况:
①使用一个已创建完毕的对象来初始化一个新对象。
②值传递的方式给函数参数传值。
③以值的方式返回局部对象。
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 深拷贝与浅拷贝
- 浅拷贝:简单的赋值拷贝。
- 深拷贝:在堆区重新申请空间,进行拷贝。
❗ 浅拷贝存在的问题:堆区内容重复释放。
✅解决方案·
//eg.
class person
{
public:person(int a,int h){age = a;height = new int(h);cout << "有参构造函数的调用" << endl;}person(const person& p){age = p.age;//height=p.height;//编译器默认实现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 初始化列表
//eg.
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 类对象作为类的成员
//eg.
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
里面有其他类,则先构造其他类,再构造自身。析构相反。
//eg
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 静态成员
- 静态成员变量:
🔵特点:
①所有对象共享一份数据。
②在编译阶段分配内存。
③类内声明,类外初始化。
//eg.
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
🔴注意:
① 静态成员变量不属于某一个对象。因此有两种访问方式:①类名访问,②对象访问。
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
②静态成员变量也有访问权限。
- 静态成员函数:
🔵特点:
①所有对象共享一个函数。
②静态成员函数只能访问静态成员函数。
🔴注意:静态成员函数也有访问权限。
//静态成员函数的访问
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;//err,对象不明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++中,类的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。
//eg.
class person1
{};
class person2
{int a;//非静态成员变量
};
class person3
{int a;static int b;//静态成员变量
};
class person4
{int a;static int b;//静态成员变量void test()//非静态成员函数{}
};
int main()
{//空对象占用内存是 1//C++会给每一个空对象分配一个字节的内存空间,为了区分空对象占内存的位置//每一个空对象也应该有一个独一无二的内存地址person1 p1;cout << "sizeof(p)=" << sizeof(p1) << endl;//1//虽然空对象有一个字节,但是一旦类里面不为空就跟着类中字节走person2 p2;cout << "sizeof(p)=" << sizeof(p2) << endl;//4//静态成员变量不属于类对象上的person3 p3;cout << "sizeof(p)=" << sizeof(p3) << endl;//4//类的成员变量和成员函数分开存储person4 p4;cout << "sizeof(p)=" << sizeof(p4) << endl;//4return 0;
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344
4.4.2 this指针概念
定义:this指针指向被调用的成员函数所属的对象。
🔵特点:
①this指针隐含在每一个非静态成员函数内的一种指针。
②this指针不需要定义,可直接使用。
🟦意义:
①当形参和成员变量同名时,可以用this指针来区分。
②在类的非静态成员函数中返回对象本身,可以用retrun *this
❗形参和成员变量名字相同:
✅解决方案:
//eg.当形参和成员变量同名时,可以用this指针来区分。
class person
{
public:person(int age){//this 指向被调用的成员函数所属的对象this->age = age;}int age;
};
int main()
{person p1(18);cout << "p1的年龄为:" << p1.age << endl;return 0;
}123456789101112131415161718
// 在类的非静态成员函数中返回对象本身,可以用retrun *this
class person
{
public:person(int age){this->age = age;}person& test(person p)//person一定要加&,使用本体{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++中空指针可以调用成员函数
class person
{
public:void test1(){cout << "test1" << endl;}void test2(){cout << "age="<<this->age<< endl;//err,传入的指针为空}int age;
};
int main()
{person *p=NULL;p->test1();p->test2();return 0;
}
1234567891011121314151617181920void test2(){if(this->age==NULL)return;//提高代码的健壮性cout << "age="<<this->age<< endl;//err,传入的指针为空}
123456
4.4.4 const修饰成员函数
常函数:成员函数后const
🔵特点:
①常函数内不可以修改成员属性。
②成员属性声明时加关键字mutable
后,在常函数中依然可以修改。
//eg.
class person
{void test2(){a = 100;b = 200; //err b是常量不可以修改c = 300;}//在成员函数后加const,修饰的是this指向,让指针指向的值不能改变void test1() const{a = 100; //err 相当于this->a=100 b = 200; //err b是常量不可修改c = 300;}int a;const int b;mutable int c;
};
int main()
{person p;p.test1();p.test2();return 0;
}
123456789101112131415161718192021222324252627
常对象:声明对象前加const
🔵特点:
①常对象只能调用常函数。
//eg.
class person
{
public:void test1(){}void test2() const{}int a;const int b;mutable int c;
};
int main()
{const person p;//不能修改指针指向的值p.a = 100;//errp.b = 200;//errp.c = 300;p.test1();//err 常对象只能调用常函数p.test2();
}
1234567891011121314151617181920212223
4.4 友元
目的:让一个函数或类,访问另一个类中的私有成员。
关键字:friend
实现:
①全局函数做友元。
②类做友元。
③成员函数做友元。
//全局函数做友元
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;//err 不可访问私有成员
}
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;//err 没有访问权限
}
//友元
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;//err
}
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 加号运算符重载
作用:实现两个自定义数据类型相加的运算。
//eg.
//加号运算符重载
class person
{
public:person(int x, int y) :a(x), b(y){}//重载运算符operator//成员函数重载+号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 左移运算符重载
作用:可以输出自定义类型。
//eg
class person
{
public://舍弃成员函数重载,p.operator<<cout相当于p<<cout与期望不相符//void operator<<(cout)//{//}person(int x) :a(x){}int a;int b;
};
//全局函数重载左移运算符
//cout属于标准输出流ostream类型
//返回ostream是链式编程思想,返回后cout<<p<<...后面可以再利用
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 递增运算符重载
//重载递增运算符
class MyInt
{friend ostream& operator<<(ostream& cout, MyInt m);
public:MyInt(){num = 0;}//前置++//&的作用是对同一个数据进行递增MyInt& operator++(){//先++num++;//再返回return *this;}//后置++//int是占位参数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=对属性进行值拷贝
//赋值运算符的重载
class person
{
public:person(int a){age=new int(a);}~person(){if (age != NULL){delete age;age = NULL;}}//重载赋值运算符以避免浅拷贝带来的问题person& operator=(person& p){//age=p.age;//编译器默认实现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;//10cout << *(p3.age) << endl;//10return 0;
}
1234567891011121314151617181920212223242526272829303132333435363738394041
4.5.5 关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作。
//关系运算符重载 == !=
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;}elsereturn false;}string name;int age;
};
int main()
{person p1("xi",10);person p2("xi",20);if (p1 == p2){cout << "p1和p2相等" << endl;}elsecout << "p1和p2不相等" << endl;return 0;
}
1234567891011121314151617181920212223242526272829303132
4.5.6 函数调用运算符重载(仿函数)
//函数调用运算符重载
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 继承的基本语法
例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同。接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处。
//普通实现页面
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 继承方式
一共有三中继承方式: ①公共继承②保护继承③私有继承
//eg.
class father
{
public:int a;
protected:int b;
private:int c;
};//公共继承
class son1 :public father
{
public:void test1(){a = 10;//父类中的公共权限成员,子类也是公共权限b = 10;//父类中的保护权限成员,子类也是保护权限//c = 10;//err 父类的私有权限成员,子类无法访问}
};
void test11()
{son1 s;s.a;//s.b;//err保护权限,类外无法访问
}//保护继承
class son2 :protected father
{
public:void test2(){a = 10;//父类中的公共权限成员,子类是保护权限b = 10;//父类中的保护权限成员,子类是保护权限//c = 10;//err 父类的私有权限成员,子类无法访问}
};
void test22()
{son2 s;//s.a;//err保护权限,类外无法访问//s.b;//err保护权限,类外无法访问
}//私有继承
class son3 :private father
{
public:void test3(){a = 10;//父类中的公共权限成员,子类是私有权限b = 10;//父类中的保护权限成员,子类是私有权限//c = 10;//err 父类的私有权限成员,子类无法访问}
};
void test22()
{son3 s;//s.a;//err私有权限,类外无法访问//s.b;//err私有权限,类外无法访问
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
4.6.3 继承中的对象模型
❗ Q:从父类继承过来的成员,哪些属于子类对象中?
//eg
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;//16return 0;
}
1234567891011121314151617181920212223
用工具查看:
cl /d1 reportSingleClassLayout查看的类名 所属文件名
1
4.6.4 继承中构造和析构顺序
//eg
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 继承同名成员处理方式
//eg
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;//通过子类对象访问父类对象成员需要加作用域//同名函数处理方式1hjs.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,...
//eg .用法与上面的大同小异,多继承可能会引发父类中有同名成员出现,需要加作用域区分
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:菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
✅:虚继承可以解决菱形继承问题
//eg.
class animal
{
public:int age;
};
//关键字virtual将yang变成虚继承
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;//20cout << yt.tuo::age << endl;//20cout << yt.age << endl;//20
}
1234567891011121314151617181920
4.7 多态
4.7.1 多态的基本概念
分类:
①静态多态:函数重载 和 运算符重载
①动态多态:派生类和虚函数实现运行时多态
🔵特点:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
//eg
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();
}
//地址晚绑定 在函数前加 virtual,在运行阶段确定地址
void DoBreathe(animal& a)
{a.breathe();
}
int main()
{cat c;DoSpeak(c);//miaomiao~DoBreathe(c);//动物呼吸
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344
动态多态满足条件:
①有继承关系
②子类重写父类中的虚函数(子类的virtual
可有可无)
动态多态的使用:父类的指针或引用,执行子类对象。
4.7.2 多态原理剖析(图解)
4.7.3 多态案例一:计算器类
案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
//普通写法 + - * /
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
❗局限:扩展新的功能,需要修改源码。
✅:
//多态写法
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 ;
抽象类定义:当类中有了纯虚函数,这个类也称为抽象类
🔵抽象类的特点:
①无法实例化对象
②子类必须重写抽象类中的纯虚函数,否则也属于抽象类
class base
{
public://纯虚函数virtual void func() = 0;
};
class son1 :public base
{
public:
};
class son2 :public base
{
public:void func() {}
};
int main()
{//base b;//err 抽象类无法实例化对象//new base;//err //son1 s;//子类必须重写抽象类中的纯虚函数,否则也属于抽象类son2 s;
}
123456789101112131415161718192021222324
4.7.5 多态案例二:制作饮品
案例描述:
制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料。
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶。
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;
类名::~类名(){}
class Animal
{
public:Animal(){cout << "Animal构造函数的调用" << endl;}~Animal(){cout << "Animal析构函数的调用" << endl;}//虚析构可以解决 父类指针释放子类对象时不干净的问题//virtual ~Animal()//{// cout << "Animal虚析构函数的调用" << endl;//}//纯虚析构//virtual ~Animal() = 0;virtual void speak() = 0;};
//纯虚析构函数要有具体的函数实现
//Animal::~Animal()
//{
// cout << "Animal纯虚析构函数的调用" << endl;
//}
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厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作
class CPU
{
public:virtual void calulate() = 0;
};
class VideoCard
{
public:virtual void display() = 0;
};
class Memory
{
public:virtual void storage() = 0;
};
//intel 厂商
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;}
};
//XiaoMi厂商
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 | 二进制方式 |
🔴注意:文件打开方式可以配合使用,利用|
操作符。
//eg.
#include <fstream>
int main()
{//1.头文件//2.创建流对象ofstream ofs;//3.打开文件,方式为:为写ofs.open("test.txt", ios::out);//4.写数据ofs << "姓名:张三" << endl;ofs << "性别:男" << endl;ofs << "年龄:18" << endl;//5.关闭文件ofs.close();return 0;
}
1234567891011121314151617
5.1.2 读文件
读文件步骤如下:
- 包含头文件:
#include <fstream>
- 创建流对象:
ifstream ifs;
- 打开文件并判断文件是否打开成功:
ifs.open(“文件路径”,打开方式);
- 读数据: 四种方式读取
- 关闭文件:
ifs.close();
//eg.
#include <fstream>
#include <string>
void test()
{//1.包含头文件//2.创建流对象ifstream ifs;//3.打开文件并判断文件是否打开成功ifs.open("test.txt", ios::in);if (!ifs.is_open()){cout << "文件打开失败" << endl;return;}//4.读数据//第一种方式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)//end of file{cout << c;}//5.关闭文件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
是读写的字节数
#include <fstream>
#include <string>
class Person
{
public:char m_Name[64];int m_Age;
};
void test01()
{//1、包含头文件//2、创建输出流对象ofstream ofs("person.txt", ios::out | ios::binary);//3、打开文件//ofs.open("person.txt", ios::out | ios::binary);Person p = { "张三" , 18 };//4、写文件ofs.write((const char*)&p, sizeof(p));//5、关闭文件ofs.close();
}
int main()
{test01();return 0;
}
1234567891011121314151617181920212223242526
5.2.2 读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer,int len);
参数解释:字符指针buffer
指向内存中一段存储空间。len
是读写的字节数
#include <fstream>
#include <string>
class Person
{
public:char m_Name[64];int m_Age;
};
void test01()
{//1.包含头文件//2.创建流对象//3.打开文件并判断文件是否打开成功ifstream ifs("person.txt", ios::in | ios::binary);if (!ifs.is_open()){cout << "文件打开失败" << endl;return;}//4.读文件(读到的是正常的不是乱码)Person p;ifs.read((char*)&p, sizeof(p));cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;//5.关闭文件ifs.close();
}
int main()
{test01();return 0;
}
12345678910111213141516171819202122232425262728293031
文章知识点与官方知识档案匹配,可进一步学
相关文章:

黑马程序员C++核心编程学习笔记
黑马程序员C核心编程学习笔记 一、内存 1.1 内存四区 C程序在执行时,将内存大致分为4个区域:代码区,全局区,栈区,堆区 代码区:存放函数体的的二进制代码,操作系统管理。 🔵特点&a…...

六自由度平台
力姆泰克六自由度平台 安装方便,维护简单 多重机械电气安全保护 向下翻动查看更多 力姆泰克伺服系统集成 全新革命性结构设计与六轴先进伺服控制原理的结合,力姆泰克公司引进国外的专业技术在国内全新推出 全电动六自由度平台。将完全替代市场上原有的…...
【Node.js 下载及npm安装配置】亲测可用
Node.js 下载及npm安装配置 安装nodejs设置安装angular 安装nodejs 下载适用自己系统的node.js,官网:https://nodejs.cn/download/。默认安装即可。查看是否安装成功,node -v,npm -v ,出现版本号即安装成功。 设置 …...
Qt C++设计模式->访问者模式
访问者模式(Visitor Pattern)是一种行为型设计模式,它将操作与对象结构分离,使得你可以在不改变对象结构的前提下定义作用于这些对象的新操作。访问者模式通过引入一个访问者对象,允许你在不修改类的前提下向已有类添加…...
手机在网状态的详细应用场景有哪些?
手机在网状态的详细应用场景涵盖了多个行业和领域,以下是一些具体的例子: 金融行业 风控审核:银行、贷款公司等金融机构在审批贷款或信用卡时,可以通过查询手机在网状态来验证申请人的手机号码是否真实有效,从而降低欺…...

Linux的kafka安装部署
1.kafka是一个分布式的,去中心化的,高吞吐低延迟,订阅模式的消息队列系统 确保要有jdk与zookeeper安装配置 2.下载kafka安装包 http://archive.apache.org/dist/kafka/2.4.1/kafka_2.12-2.4.1.tgz 此时可以wget http://archive.apache.org/dist/kafka/2.4.1/kafka_2.12-2.4.…...

docker部署虚拟机
创建新的容器web02,-v表示目录映射,-p时端口映射,把宿主机目录挂载到容器中 docker run -itd -p 80:80 -v /data/webapps/www/:/usr/share/nginx/html/ --nameweb02 nginx:latest 此时我们在发布网站时只需要放在宿主机的目录里就可以了 解…...

如何用ChatGPT 8小时写出一篇完整论文(附完整提示词)
今天教大家如何利用ChatGPT完成一篇完整的论文。只需要一个标题,剩下全部由ChatGPT完成。总耗时8小时。 阅前提醒: 1.适用人群:这个方法适合应付简单的学术任务,比如日常小论文或投稿一般期刊。但如果你要写高水平的论文…...

AWS MySQL 升级(三)—— TAZ - 近0停机的小版本升级方案
与AWS交流了解到的新方案,没有实际试过,所以本篇主要是些原理 一、 TAZ的含义 TAZ实际上就是 3 AZ,扩展一些就是 Multi-AZ DB Cluster,即在3个可用区部署DB,具备两个只读备用实例。 二、 TAZ的主要用途 1. 近0停机的小…...
Redis的应用以及Redis工具类的封装
在前后端分离的项目中,通过session和cookie的通信一般就失去效益了,即使这么做了也会产生著名的漏洞问题CSRF(Cross-site request forgery), 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。因…...

E系列I/O模块在锂电装备制造系统的应用
为了满足电池生产线对稳定性和生产效率的严苛要求,ZLG致远电子推出高速I/O应用方案,它不仅稳定可靠,而且速度快,能够迅速响应生产需求。 锂电池的生产工艺较为复杂,大致分为三个主要阶段:极片制作、电芯制作…...
ElasticsearchClient入门指南
在本教程中,我们将探讨如何使用Elasticsearch的官方Java客户端 - ElasticsearchClient。这个强大的工具允许您的Java应用程序与Elasticsearch集群进行交互,执行各种操作,如索引文档、执行搜索查询等。 前提条件 在开始之前,确保您的项目中已经包含了必要的依赖。您可以通过Ma…...

软考中级笔记
上午题 二 程序设计语言 6′ 1、编译程序和解释程序 解释器:翻译源程序时不生成独立的目标程序。 解释程序和源程序要参与到程序的运行过程中。 编译器:翻译时将源程序翻译成独立保存的目标程序。 机器上运行的是与源程序等价的目标程序。 源程序和编…...

学习python自动化——pytest单元测试框架
一、什么是pytest 单元测试框架,unittest(python自带的),pytest(第三方库)。 用于编写测试用例、收集用例、执行用例、生成测试结果文件(html、xml) 1.1、安装pytest pip instal…...
定位、地图建立及管理合集
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言机器人中常见的定位技术介绍一、直方图定位原理二、gnss、rtk定位原理三、【依赖二维激光雷达与全局地图的定位算法】amcl&am…...
uniAPP是否可以做php语言书写后端的前端开发?
UniAPP可以做PHP语言书写后端的前端开发。以下是关于这个问题的详细解答: 一、UniAPP与后端开发的关系 前后端分离:UniAPP作为一款跨平台开发框架,采用了前后端分离的开发模式。这意味着前端和后端的开发可以独立进行,互不影响。…...

柒拾伍- AI内容农场生产文章自动发布至公众号 (一)
一、内容农场 X AI 看过很多的新闻说 AI 产生 内容 污染网络,我也想试一下到底能污染成怎样。 然后为了编写爆款的内容,我选用这个 内容农场 的种子是来源于 微博热搜,让生长出来的垃圾文章更加火爆 涉及内容不能放 二、编写代码 关于代…...
java.util.function Function<T, R>
一、介绍 1、简介 Function<T, R> 是 Java 8 中的一个函数式接口,用于表示接受一个输入参数 T,并返回一个结果 R 的函数。Function接口中有一个抽象方法apply,用于定义函数的逻辑。Function接口通常用于将数据进行转换(处…...

Allegro在PCB上开槽的三种方法操作指导
Allegro如何在PCB上开槽的三种方法操作指导 当PCB有特殊设计要求的时候,需要在PCB上开槽,Allegro支持在PCB上开槽操作,具体操作如下 以下图为例,需要在这个板框中间开槽 开方形槽 选择shape add rect命令 画在Board Geometry-o…...

Docker:快速部署
docker安装: 安装Docker - 飞书云文档 (feishu.cn) docker命令解读 docker run -d \ > --name mysql \ > -p 33…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...