基本代码框架
#include
using namespace std;
int main()
{
cout << "hello world" << endl; //endl为换行
system("pause");
return 0;
}
注释同java
变量存在的意义:方便我们管理内存空间
变量创建的语法:数据类型 变量名=变量初值
int a=10;
用于记录程序中不可更改的数据
两种方式:
1:#define 宏常量:# define 常量名 常量值
2:const修饰的变量:const 数据类型 常量名=常量值
是c++中预先保留的单词
cpp规定给标识符命名时,有一套自己的规则
给变量分配合适的内存空间
表示的是整数类型的数据
数据类型 | 占用空间 | 取值方位 |
---|---|---|
short | 2字节 | -2^15 ~ 2^15-1 |
int | 4字节 | -2^31 ~ 2^31-1 |
long | windows为4字节,Linux为4字节(32位),8字节(64位) | -2^31 ~ 2^31-1 |
long long | 8字节 | -2^63 ~ 2^63-1 |
可以统计数据类型所占内存大小
整形结论: short
用于表示小数
两者区别在于有效数字范围不同
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15~16有效数字 |
默认输出一个小数,显示六位有效数字
//科学计数法
float f2=3e2; // 3*100;
字符型变量用于显示单个字符
char ch=‘a’;
用于表示一些不能显示出来的ASCII字符
//换行符 \n
// 反斜杠 \\
// 水平制表符 \t 作用可以整齐输出数据
用于表示一串字符
两种风格
代表真或假
bool占一个字节大小
bool flag=true;(本质为1)
用于从键盘获取数据
关键字:cin
语法:cin>>变量 n
用于执行代码的运算
运算符类型 | 作用 |
---|---|
算术运算符 | 用于处理四则运算 |
赋值运算符 | 用于将表达式的值赋给变量 |
比较运算符 | 用于表达式的比较,并返回一个真值或假 |
逻辑运算符 | 用于根据表达式的值返回真值或假值 |
用于处理四则运算
用于将表达式的值赋给变量
用于表达式的比较,并返回一个真值或假值
用于根据表达式的值返回真值或假值
执行满足条件的语句
通过三目运算符实现简单的判断
语法:表达式1?表达式2:表达式3
执行多条件分支语句
语法:
switch(表达式){
case 结果1:执行语句;break;
case 结果2:执行语句;break;
...
default:执行语句;break;
}
switch语句中表达式类型只能是整形或字符型
满足循环条件,执行循环语句
语法:while(循环条件){循环语句}
解释:只要循环条件的结果为真,就执行循环语句
#include
//添加随机数种子,利用当前系统时间生成随机数,防止每次都一样
srand((unsigned int)time(NULL));
rand()%100 //生成0~99随机数
满足循环条件,执行循环语句
语法:do{循环语句}while(循环条件);
与while的区别在于do…while会执行以此循环语句,再判断循环条件
满足循环条件,执行循环语句
语法:for(起始表达式;条件表达式;末尾循环体){循环语句;}
在循环体中再嵌套一层循环,解决一些实际问题
用于跳出选择结构或者循环结构
在循环语句中,跳过本次循环中余下未执行的语句,继续执行下一次循环
可以无条件跳转语句
语法:goto 标记;
所谓数组,就是一个集合,里面存放了相同类型的数据元素
三种方式:
一维数组名称的用途
作用:最常用的排序算法,对数组内元素进行排序
四种方式
1.数据类型 数组名[行数][列数]
2.数据类型 数组名[行数][列数] ={ {数据1,数据2},{数据3,数据4} };
4.数据类型 数组名[][列数]={数据1,数据2,数据3,数据4};
建议第二种,提高代码可读性
查看二维数组所占内存空间
获取二维数组首地址
查看二维数组第一个元素地址
(int)&arr[0][0] //取址符&
将一段经常使用的代码封装起来,减少重复代码
函数的定义一般主要有5个步骤
语句
返回值类型 函数名(参数列表)
{
函数体语句
return 表达式
}
使用定义好的函数
语法:函数名(参数)
常见的函数样式有四种
告诉编译器函数名称及如何调用函数。函数的实际主题可以单独定义
int max(int a,int b); // 提前告诉编译器函数存在,这样在主函数后可以定义函数
让代码结构更加清晰
函数分文件一般有三个步骤
指针的作用:可以通过指针直接访问内存
语法: 数据类型 * 指针变量名;
使用:可以通过解析引用的方式来找到指针指向的内存
指针前加*代表解引用,找到指针指向的内存中的数据
32 位占4个字节空间
64位占 8个字节空间
空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
0~255是系统占用的
野指针:指针变量指向非法的内存空间
// 在程序中,尽量避免出现野指针
int *p=(int *)0x1100;
const修饰指针有三种情况:
1.const修饰指针 --常量指针
const int *p =&a;
特点:指针的指向可以修改,但是指针指向的值不可以更改
2.const修饰常量 --指针常量
int * const p= &a;
特点:指针的指向不可以改,指针指向的值可以改
3.const修饰指针,又修饰常量
const int* const p=&a;
特点:指针的指向和指针指向的值都不可以更改
利用指针访问数组中元素
#include
using namespace std;
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr; //arr就是数组首地址
cout << *p << endl;
p++;
cout << *p << endl; // 让指针向后偏移4个字节
int* p2 = arr; // 利用指针循环遍历
for (int i = 0; i < 10; i++) {
cout << *p2;
p2++;
}
system("pause");
return 0;
}
作用:利用指针作函数参数,可以修改实参的值
地址传递:
如果是地址传递,可以修饰实参
总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递
结构体属于用户自定义的数据类型,允许用户存储不同的数据结构
语法:struct 结构体名 {结构体成员变量};
通过结构体创建变量的三种方式:
#include
using namespace std;
struct Student
{
//成员列表
string name;
int age;
int score;
}s3;
int main()
{
//1 创建方式
// struct Student s1; 关键字struct可以省略
struct Student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 88;
cout << s1.name << s1.age<< s1.score << endl;
// 2 struct Student s2={```}
struct Student s2 = { "里斯",19,99 };
cout << s2.name << s2.age << s2.score << endl;
// 3 创建结构体同时创建变量
s3.name = "李白";
s3.age = 18;
s3.score = 99;
cout << s3.name << s3.age << s3.score << endl;
system("pause");
return 0;
}
总结1:定义结构体时的关键字时struct,不可省略
总结2:创建结构体变量时,关键字struct可以省略
总结3:结构体变量利用操作符“.”访问成员
将自定义的结构体放入到数组中方便维护
语法:struct 结构体名 数组名[元素个数]={ {}, {}, {}…}
#include
using namespace std;
struct Student
{
string name;
int age;
int score;
};
int main()
{
struct Student stdArray[3] = {
{"张三",18,100},
{"里斯",28,99},
{"王五",10,100}
};
stdArray[2].name = "赵六";
stdArray[2].age = 80;
stdArray[2].score = 60;
for (int i = 0; i < 3; i++) {
cout << stdArray[i].name << stdArray[i].age << stdArray[i].score << endl;
}
system("pause");
return 0;
}
通过指针访问结构体中的成员
#include
using namespace std;
struct Student
{
string name;
int age;
int score;
};
int main()
{
struct Student s = { "张三",18,99 };
struct Student *p = &s;
cout << p->name << p->age << p->score << endl;
system("pause");
return 0;
}
结构体中的成员可以是另一个结构体
将结构体作为参数向函数中传递
传递方式有两种:
#include
using namespace std;
struct Student
{
string name;
int age;
int score;
};
// 1 值传递
void printStudent1(struct Student s)
{
cout << s.name << s.age << s.score << endl;
};
//2 地址传递
void printStudent2(struct Student *p)
{
cout << p->name << p->age<< p->score << endl;
};
int main()
{
struct Student s = {"董斌",18,99};
printStudent1(s);
printStudent2(&s);
system("pause");
return 0;
}
** 总结:如果不想修改主函数中的数据,用值传递,反之用地址传递**
用const防止误操作,将函数中形参改为指针,可以减少内存空间,而且不会复制新的副本出来。
#include
using namespace std;
struct Student
{
string name;
int age;
int score;
};
void printStudent2(const struct Student* p)
{
// 加入const之后,可以防止误操作
cout << p->name << p->age << p->score << endl;
};
int main()
{
struct Student s = {"董斌",18,99};
printStudent2(&s);
system("pause");
return 0;
}
主要针对C++面向对象编程技术
c++程序在执行时,将内存大方向划分为四个区域
内存四区意义:
不同区域存放的数据,赋予不同的生命周期,给我们更加大的灵活编程
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区:
存放 CPU执行的机器指令。
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放于此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统回收
#include
using namespace std;
// 全局变量
int g_a = 10;
int g_b = 10;
const int c_g_a = 10;
int main() {
// 全局区
//全局变量、静态变量、常量
//创建普通局部变量
int a = 10;
int b = 10;
cout << "局部变量a的地址为: " << (long)&a << endl;
cout << "局部变量b的地址为: " << (long)&b << endl;
cout << "全局变量g_a的地址为: " << (long)&g_a << endl;
cout << "全局变量g_b的地址为: " << (long)&g_b << endl;
// 静态变量 在普通变量前加static
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a的地址为: " << (long)&s_a << endl;
cout << "静态变量s_b的地址为: " << (long)&s_b << endl;
//常量
//字符串常量
cout << "字符串常量的地址为: " << (long)&"hello world" << endl;
//const修饰的变量
//const修饰的全局变量
cout << "全局常量的地址为: " << (long)&c_g_a << endl;
const int c_l_a = 10;
cout << "局部常量的地址为: " << (long)&c_l_a << endl;
system("pause");
return 0;
}
栈区:
#include
using namespace std;
// 栈区数据注意事项 -- 不要释放局部变量地址
//数据由编译器管理开辟和释放
int* fun(int b) { // 形参数据也会放在栈上
b = 100;
int a = 10;//局部变量,存放在栈区,栈区的数据在函数执行完自动释放
return &a; //返回局部变量的地址
}
int main() {
int* p = fun(1);
cout << *p << endl; //第一次可以打印正确,编译器做了保留
cout << *p << endl; //第二次这个数据就不再保留
system("pause");
return 0;
}
堆区:
#include
using namespace std;
int* func() {
// 利用new关键字,可以将数据开辟到堆区
//指针,本质为变量,放在栈上,指针保存的数据是放在堆区
int *p=new int(10);
return p;
}
int main() {
//在堆区开辟数据
int* p = func();
cout << *p << endl;
system("pause");
return 0;
}
总结:
c++利用new操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
#include
using namespace std;
// 1 new基本语法
int* fun() {
//在堆区创建整形数据
int *p=new int(10);
return p;
}
void test01() {
int* p = fun();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;
//堆区数据由程序员管理开辟,程序员管理释放
//如果想释放堆区的数据,利用关键字delete
delete p;
/*cout << *p << endl;*/ //内存已经释放,再次访问就是非法操作,会报错
}
void test02() {
//创建10整形数据的数组,在堆区
int *arr=new int[10]; //10代表数组有10个空间
for (int i = 0; i < 10; i++) {
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++) {
cout << arr[i] << endl;
}
//释放堆区数组
//释放数组的时候,要加[]才可以
delete[] arr;
}
int main() {
test02();
system("pause");
return 0;
}
作用:给变量起别名
语法:数据类型 &别名=原名
#include
using namespace std;
int main() {
//引用
int a = 10;
int& b = a;
cout << a << endl;
cout << b << endl;
b = 100;
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
#include
using namespace std;
int main() {
int a = 10;
//1引用必须初始化
int& b = a;
//2引用在初始化后,不可以改变
int c = 20;
system("pause");
return 0;
}
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
#include
using namespace std;
//交换函数
// 1.值传递
void swap01(int a,int b) {
int temp = a;
a = b;
b = temp;
}
// 2、地址传递
void swap02(int *a,int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
//3、引用传递
void swap03(int &a,int&b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
cout << a << endl;
cout << b << endl;
swap03(a, b); //引用传递,形参会修饰实参的
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
总结:通过引用参数产生的效果同按地址传递是一样的,引用的语法更清楚简单。
作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
#include
using namespace std;
int& test01() {
int a = 10;
return a;
}
//函数调用可以作为左值
int& test02() {
static int a = 10;//全局区,程序结束后系统释放
return a;
}
int main() {
int& ref = test02();
cout << ref << endl; //第一次正确,编译器做了保留
cout << ref << endl; //第二次错误,因为a的内存已经释放
test02() = 1000; //如果函数返回值是引用,这个函数条用可以作为左值
cout << ref << endl;
cout << ref << endl;
system("pause");
return 0;
}
本质:引用的本质在C++内部实现是一个指针常量
结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,方式形参改变实参
#include
using namespace std;
void swap(const int &value) {
// value = 1000;
cout << value << endl;
}
int main() {
// 加上const之后,编译器将代码修改int temp=10,const int &ref=temp;
//const int& ref = 10; //引用必须引一块合法的内存空间
//ref=10; // 加入const之后变为只读,不可以修改
int a = 10;
swap(a);
cout << a << endl;
system("pause");
return 0;
}
在C++中,函数的形参列表中的形参是可以有默认值的
语法:返回值类型 函数名 (参数=默认值){}
#include
using namespace std;
//函数默认参数
//如果我们自己传入数据,就用自己的数据,如果没有,那么用默认值
int func(int a, int b=20, int c=30) {
return a + b + c;
}
// 注意事项
//1,如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
// int func2(int a=10, int b, int c) {
// return a + b + c;}
//2.如果函数声明有默认参数,函数实现就不能有默认函数
//声明和实现只能一个有默认参数
//int func2(int a=10, int b=10);
//int func2(int a=10, int b=10) {
// return a + b;
//}
int main() {
cout << func(10) << endl;
system("pause");
return 0;
}
c++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法: 返回值类型 函数名(数据类型){}
作用:函数名可以相同,提高复用性
函数重载满足条件:
注意:函数的返回值不可以作为函数重载的条件
#include
using namespace std;
void func() {
cout << "func调用" << endl;
}
void func(int a) {
cout << "funca调用!" << endl;
}
void func(double a) {
cout << "double调用!" << endl;
}
void func(int a,double b) {
cout << "1调用!" << endl;
}
void func(double a, int b) {
cout << "2调用!" << endl;
}
//注意事项:函数返回值不可以作为函数重载的条件
int main() {
func(1.0,1);
system("pause");
return 0;
}
#include
using namespace std;
//函数重载注意事项
//1.引用作为重载条件
void func(int &a) {
cout << "func(&a)调用" << endl;
}
void func(const int& a) {
cout << "func(const &a)调用" << endl;
}
//2.函数重载碰到默认参数
void func2(int a,int b=10) {
cout << "func(int a)的调用" << endl;
}
void func2(int a) {
cout << "func(int a)的调用" << endl;
}
int main() {
/*int a = 10;
func(a);*/ // func(&a)调用
func(10); // func(const &a)调用
func2(10); //函数重载碰到默认参数,出现二义性,报错,尽量避免这种情况
system("pause");
return 0;
}
C++面向对象的三大特性:封装、继承、多态
C++认为万事万物皆为对象,对象上有其属性和行为
封装是C++面向对象三大特性之一
封装的意义:
封装意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法:class 类名{访问权限:属性/行为};
#include
using namespace std;
const double PI = 3.14;
//设计一个圆类,求圆的周长
//圆求周长公式:2*pi*半径
//class代表设计一个类,类后紧跟着是类名称
class Circle {
//访问权限
public:
//属性
//半径
int m_r;
//行为
//获取圆的周长
double calculateZC() {
return 2 * PI * m_r;
}
};
int main() {
//通过圆类 创建具体的圆(对象)
//实例化:通过一个类创建一个对象的过程
Circle c1;
// 给圆对象的属性赋值
c1.m_r = 10;
cout << "圆的周长为:" << c1.calculateZC() << endl;
system("pause");
return 0;
}
封装意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
#include
using namespace std;
// 三种访问权限
// 公共权限 public 成员类内可以访问,类外可以访问
// 保护权限 protected 成员 类内可以访问,类外不可以访问 儿子也可以访问父亲保护内容
// 私有权限 private 成员 类内可以访问,类外不可以访问 儿子不可以访问父亲私有内容
class Person {
public:
//公共权限
string m_name; //姓名
protected:
//保护权限
string m_Car;//汽车
private:
//保护权限
int m_Password;//银行卡密码
public:
void func() {
m_name = "张三";
m_Car = "拖拉机";
m_Password - 123456;
}
};
int main() {
//实例化具体对象
Person p1;
p1.m_name = "李四";
//p1.m_Car = "奔驰"; // 保护权限内容,在类外访问不到
//p1.m_Password = 1232; //私有权限内容,在类外访问不到
system("pause");
return 0;
}
在C++中struct和class唯一的区别就在于默认访问权限不同
区别:
#include
using namespace std;
class C1 {
int m_A; // 默认权限是私有
};
struct C2 {
int m_A;//默认权限是公有
};
int main() {
//struct 和class区别
//struct默认权限是 公共 public
//class默认权限是 私有 private
C1 c1;
/*c1.m_A = 100;*/
C2 c2;
c2.m_A = 100;
system("pause");
return 0;
}
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
C++的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置
对象的初始化和清理是两个非常重要的安全问题。
C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现
构造函数语法:类名(){}
析构函数语法:~类名(){}
#include
using namespace std;
//对象的初始化和情理
//1.构造函数 进行初始化操作
class Person {
// 1.1 构造函数
//没有返回值 不用写Void
//函数名 与类名相同
//构造函数可以有参数,可以发生重载
//创建对象的时候,构造函数会自动调用,而且只调用一次
public:
Person() {
cout << "构造函数调用" << endl;
}
//2.析构函数 进行清理的操作
//没有返回值 不写void
//函数名和类名相同 在名称前加~
//析构函数不可以有参数的,不可以发生重载
//对象销毁前 会自动调用析构函数,而且只会调用一次
~Person() {
cout << "析构函数调用" << endl;
}
};
void test01() {
Person p; //在栈上对象,test01执行完毕后,释放这个对象
}
int main() {
test01();
system("pause");
return 0;
}
两种分类方式:
三种调用方式:
#include
using namespace std;
//1 构造函数的分类及调用
//分类
// 按照参数分类 无参构造(默认构造)和有参构造
//按照类型分类 普通构造 拷贝构造
class Person {
// 构造函数
public:
Person() {
cout << "Person的无参构造函数调用" << endl;
}
Person(int a) {
age = 10;
cout << "Person的有参构造函数调用" << endl;
}
//拷贝构造函数
Person(const Person &p) {
age = p.age;
cout << "Person的拷贝构造函数调用" << endl;
}
~Person() {
cout << "Person的析构函数调用" << endl;
}
int age;
};
// 调用
void test01() {
//1、括号法
//Person p1; //默认构造函数调用
//Person p2(10); // 有参构造函数
//拷贝构造函数
/*Person p3(p2);*/
//注意事项1
//调用默认构造函数时候,不要加()
// 创建不了对象,下面这个代码,编译器会认为是一个函数的声明,不会认为在创建对象
/*Person p1();*/
/*cout << "p2的年龄:" << p2.age << endl;
cout << "p3的年龄:" << p3.age << endl;*/
//2、显示法
//Person p1;
//Person p2 = Person(10); //有参构造
拷贝构造
//Person p3 = Person(p2);
/*Person(10);*///匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象
/*cout << "aaaaa" << endl;*/
//注意事项2
//不要利用拷贝构造函数 初始化匿名对象 编译器会认为Person(p3)==Person p3,对象声明
/*Person(p3);*/
//3、隐式转换法
Person p4 = 10; //相当于写了Person p4=Person(10);
Person p5 = p4; // 拷贝构造
}
int main() {
test01();
system("pause");
return 0;
}
C++中拷贝构造函数调用时机通常由三种情况
#include
using namespace std;
//拷贝构造函数调用时机
class Person {
public:
Person() {
cout << "默认构造函数" << endl;
}
Person(int age) {
m_Age = age;
}
Person(const Person& p) {
cout << "Person拷贝构造函数调用" << endl;
m_Age = p.m_Age;
}
~Person() {
cout << "析构函数调用" << endl;
}
int m_Age;
};
//1、使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
Person p1(10);
Person p2(p1);
cout << "p2年龄为:" << p2.m_Age << endl;
}
//2、值传递的方式给函数参数传值
void doWork(Person p) {
}
void test02() {
Person p;
doWork(p);
}
//3、值方式返回局部对象
Person doWork2() {
Person p1;
cout << (int)&p1 << endl;
return p1;
}
void test03() {
Person p = doWork2();
cout << (int)&p << endl;
}
int main() {
/*test01();*/
/*test02();*/
test03();
system("pause");
return 0;
}
默认情况下,C++编译器至少给一个类添加三个函数
构造函数调用规则如下:
#include
using namespace std;
//构造函数调用规则
//1.创建一个类,C++编译器会给每个类都添加至少三个函数
// 默认构造(空实现)
// 析构函数(空实现)
// 拷贝构造 (值拷贝)
//2、如果我们写了有参构造函数,编译器就不再提供默认构造,仍提供拷贝构造函数
//如果写了拷贝构造函数,编译器就不再提供其他构造函数
class Person {
public:
/*Person() {
cout << "默认构造函数" << endl;
}*/
Person(int age) {
m_Age = age;
}
/*Person(const Person& p) {
cout << "Person拷贝构造函数调用" << endl;
m_Age = p.m_Age;
}*/
~Person() {
cout << "析构函数调用" << endl;
}
int m_Age;
};
//void test01() {
// Person p;
// p.m_Age = 18;
// Person p2(p);
// cout << "p2年龄为" << p2.m_Age << endl;
//}
void test02() {
Person p1(18);
Person p2(p1);
cout << "p2年龄为" << p2.m_Age << endl;
}
int main() {
/*test01();*/
test02();
system("pause");
return 0;
}
浅拷贝:简单的赋值拷贝操作
(问题:堆区的内存重复释放,用深拷贝解决)
深拷贝:在堆区重新申请空间,进行拷贝操作
#include
using namespace std;
//深拷贝和浅拷贝
class Person {
public:
Person() {
cout << "默认构造函数" << endl;
}
Person(int age,int height) {
m_Age = age;
m_Height=new int(height);
cout << "有参构造函数" << endl;
}
//自己实现拷贝构造函数,解决浅拷贝带来的问题
Person(const Person& p) {
cout << "Person拷贝构造函数调用" << endl;
m_Age = p.m_Age;
//m_Height = p.m_Height; 编译器默认实现是这行代码
//深拷贝操作
m_Height=new int(*p.m_Height);
}
~Person() {
//析构代码,将堆区开辟数据做释放操作
if (m_Height != NULL) {
delete m_Height;
m_Height = NULL;
}
cout << "析构函数调用" << endl;
}
int m_Age;
int* m_Height;
};
void test01() {
Person p1(18,160);
cout << "p1的年龄为" << p1.m_Age<<"身高为:" <<*p1.m_Height<< endl;
Person p2(p1);
cout << "p2的年龄为" << p2.m_Age <<"身高为:" <<*p1.m_Height <<endl;
}
int main() {
test01();
system("pause");
return 0;
}
总结:如果属性由在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
作用:C++提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)…{}
#include
using namespace std;
//初始化列表
class Person {
public:
/*Person(int a, int b, int c) {
m_A = a;
m_B = b;
m_C = c;
}*/
//初始化列表初始化属性
Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c) {
}
int m_A;
int m_B;
int m_C;
};
void test01() {
/*Person p(10,20,30);*/
Person p(10,20,30);
cout << p.m_A << endl;
cout << p.m_B << endl;
cout << p.m_C << endl;
};
int main() {
test01();
system("pause");
return 0;
}
C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
例如
class A{}
Class B{
A a;
}
#include
#include
using namespace std;
//类对象作为类成员
//手机类
class Phone {
public:
Phone(string name) {
m_PName = name;
cout << "手机构造函数" << endl;
}
~Phone() {
cout << "phone析构函数" << endl;
}
string m_PName;
};
class Person {
public:
// Phone m_Phone=pName 隐式转换法
Person(string name, string pName) :m_Name(name),m_Phone(pName){
cout << "人的构造函数" << endl;
}
~Person() {
cout << "person析构函数调用" << endl;
}
//姓名
string m_Name;
//手机
Phone m_Phone;
};
//当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构顺序与构造相反
void test01() {
Person p("Zhaga", "sangxing");
cout << p.m_Name << endl;
cout << p.m_Phone.m_PName << endl;
};
int main() {
test01();
system("pause");
return 0;
}
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:
#include
#include
using namespace std;
class Person {
public:
// 所有对象共享同一份数据
// 编译阶段就分配内存
// 类内声明,类外初始化操作
static int m_A;
//静态成员变量也是有访问权限的
private:
static int m_B;
};
int Person::m_A = 100;
int Person::m_B = 200;
void test01() {
Person p1;
cout << p1.m_A << endl;
Person p2;
p2.m_A = 200;
cout << p1.m_A << endl;
}
void test02() {
//静态成员变量不属于某个对象上,所有对象都共享同一份数据
//因此静态成员变量有两种访问方式
// 1.通过对象进行访问
//Person p;
//cout << p.m_A << endl;
// 2.通过类名进行访问
cout << Person::m_A << endl;
// cout << Person::m_B << endl; 类外访问不到私有静态
}
int main() {
/*test01();*/
test02();
system("pause");
return 0;
}
#include
#include
using namespace std;
//静态成员函数
//所有对象共享同一个函数
//静态成员函数只能访问静态成员变量
class Person {
public:
//静态成员函数
static void func() {
m_A = 200; //静态成员函数可以访问静态成员变量
//m_B = 200; 静态成员函数不可以访问 非静态成员变量,无法区分到底是那个对象的m_B属性
cout << "static void func调用" << endl;
}
static int m_A;
int m_B;//非静态成员变量
// 静态成员函数也是有访问权限的
private:
static void func2() {
cout << "func2调用" << endl;
}
};
int Person::m_A = 100;
void test01() {
//1.通过对象进行访问
Person p;
p.func();
//2.通过类名进行访问
Person::func();
// Person::func2();类外访问不到私有静态成员函数
}
int main() {
test01();
system("pause");
return 0;
}
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
空对象大小为1
#include
#include
using namespace std;
// 成员变量和成员函数分开存储
class Person {
int m_A;//非静态成员变量 属于类的对象上
static int m_B; //静态成员变量 不属于类对象上
void func() {
}//非静态函数 不属于类对象上
static void func2() {
} // 静态成员函数,不属于类对象上
};
int Person::m_B = 0;
void test01() {
Person p;
//空对象占用内存空间为:1
// C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
//每个空对象有独一无二的内存地址
cout << sizeof(p) << endl;
}
void test02() {
Person p;
cout << sizeof(p) << endl;
}
int main() {
// test01();
test02();
system("pause");
return 0;
}
this指针指向被调用的成员函数所属的对象
this指针是隐含每个一非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针作用:
#include
#include
using namespace std;
class Person {
public:
Person(int age) {
//this指针指向被调用的成员函数所属的对象
this->age = age;
}
Person& PersonAddPerson(Person &p) {
this->age += p.age;
return *this;
}
int age;
};
// 1 解决名称冲突
void test01() {
Person p1(18);
cout << p1.age << endl;
}
// 2 返回对象本身用*this
void test02() {
Person p1(10);
Person p2(10);
// 链式编程
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
cout << p2.age << endl;
}
int main() {
test02();
system("pause");
return 0;
}
C++空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断代码的健壮性
#include
#include
using namespace std;
//空指针调用成员函数
class Person {
public:
void showName() {
cout << "Person" << endl;
}
void showAge() {
//报错原因:传入的指针为空
if (this == NULL) {
return;
}
cout << this->m_Age << endl;
}
int m_Age;
};
// 1 解决名称冲突
void test01() {
Person* p = NULL;
p->showName();
p->showAge();
}
int main() {
test01();
system("pause");
return 0;
}
常函数:
常对象:
#include
#include
using namespace std;
//常函数
class Person {
public:
//this指针的本质 是指针常量 指针的指向是不可以修改的
// Person *const this;
//在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
void showPerson() const {
this->m_B = 100;
// this->m_A = 100;
//this = NULL; //this指针不可以修改指针的指向
}
void func() {
}
int m_A;
mutable int m_B; //特殊变量,即使在常函数种,也可以修改这个值,加上关键字mutable
};
void test01() {
Person p;
p.showPerson();
}
//常对象
void test02() {
const Person p; //在对象前加const,变为常对象
// p.m_A = 100;
p.m_B = 100; //m_B是特殊值,即使在常对象下也可以修改
p.showPerson();
//p.func(); 常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性
}
int main() {
test01();
system("pause");
return 0;
}
友元的目的就是让一个函数或者类访问另一个类中私有成员
友元关键字friend
友元的三种实现
#include
#include
using namespace std;
//建筑物类
class Building {
// GoodGay全局函数是Builing好朋友,可以访问
friend void goodGay(Building* building);
public:
Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SittingRoom;//客厅
private:
string m_BedRoom; //卧室
};
//全局函数
void goodGay(Building *building) {
cout << "好基友正在访问:" << building->m_SittingRoom << endl;
cout << "好基友正在访问:" << building->m_BedRoom<< endl;
}
void test01() {
Building building;
goodGay(&building);
}
int main() {
test01();
system("pause");
return 0;
}
#include
#include
using namespace std;
//类作友元
class Building;
class GoodGay {
public:
GoodGay();
void visit();//参观函数访问Building属性
Building* building;
};
class Building {
//GoodGay类可以访问本类中私有成员
friend class GoodGay;
public:
Building();
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
//类外写成员函数
Building::Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay() {
//创建建筑物对象
building = new Building;
}
void GoodGay::visit() {
cout << building->m_SittingRoom << endl;
cout << building->m_BedRoom << endl;
}
void test01() {
GoodGay gg;
gg.visit();
}
int main() {
test01();
system("pause");
return 0;
}
operator+
#include
#include
using namespace std;
//类作友元
class Building;
class GoodGay {
public:
GoodGay();
void visit();//参观函数访问Building属性
void visit2();//不可以访问私有成员
Building* building;
};
class Building {
// 告诉编译器GoodGay下visit作为奔雷好朋友,可以访问私有成员
friend void GoodGay::visit();
public:
Building();
public:
string m_SittingRoom;
private:
string m_BedRoom;
};
//类外写成员函数
Building::Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
GoodGay::GoodGay() {
//创建建筑物对象
building = new Building;
}
void GoodGay::visit() {
cout << building->m_SittingRoom << endl;
cout << building->m_BedRoom << endl;
}
void GoodGay::visit2() {
cout << building->m_SittingRoom << endl;
/*cout << building->m_BedRoom << endl;*/
}
void test01() {
GoodGay gg;
gg.visit();
gg.visit2();
}
int main() {
test01();
system("pause");
return 0;
}
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
#include
using namespace std;
//加号运算符重载
class Person {
public:
//1.成员函数重载+号
/*Person operator+(Person& p) {
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}*/
int m_A;
int m_B;
};
//2.全局函数重载+号
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
void test01() {
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
cout << p3.m_A << endl;
cout << p3.m_B << endl;
}
int main() {
test01();
system("pause");
return 0;
}
总结1:对于内置的数据类型的表达式的运算符是不可能改变的
总结2:不要滥用运算符重载
作用:可以输出自定义数据类型
#include
using namespace std;
//左移运算符重载
class Person {
public:
//利用成员函数重载 左移运算符 一般不用,无法实现在左侧,利用全局函数实现
void operator<<(Person &p) {
}
int m_A;
int m_B;
};
ostream & operator<<(ostream &cout, Person &p) {
cout << p.m_A << p.m_B << endl;
return cout;
}
void test01() {
Person p1;
p1.m_A = 10;
p1.m_B = 10;
cout << p1 << endl;
}
int main() {
test01();
system("pause");
return 0;
}
总结:重载左移运算符配合友元可以实现输出自定义数据类型
继承是面向对象三大特性之一
#include
using namespace std;
//普通实现页面
//class Java {
//public:
// void header() {
// cout << "首页、公开课、登录、注册..(公共头部)" << endl;
// }
// void footer() {
// cout << "帮助中心、交流合作、站内地图" << endl;
// }
// void left() {
// cout << "java、Python、C++" << endl;
// }
// void content() {
// cout << "java学科" << endl;
// }
//};
//class Python {
//public:
// void header() {
// cout << "首页、公开课、登录、注册..(公共头部)" << endl;
// }
// void footer() {
// cout << "帮助中心、交流合作、站内地图" << endl;
// }
// void left() {
// cout << "java、Python、C++" << endl;
// }
// void content() {
// cout << "Python学科" << endl;
// }
//};
//class Cpp {
//public:
// void header() {
// cout << "首页、公开课、登录、注册..(公共头部)" << endl;
// }
// void footer() {
// cout << "帮助中心、交流合作、站内地图" << endl;
// }
// void left() {
// cout << "java、Python、C++" << endl;
// }
// void content() {
// cout << "C++学科" << endl;
// }
//};
//继承实现页面
//公共页面类
class BasePage {
public:
void header() {
cout << "首页、公开课、登录、注册..(公共头部)" << endl;
}
void footer() {
cout << "帮助中心、交流合作、站内地图" << endl;
}
void left() {
cout << "java、Python、C++" << endl;
}
};
//继承好处:减少重复代码
//语法: class 子类 :继承方式 父类
//子类 也成为派生类
//父类 也成为基类
class Java :public BasePage {
public:
void content() {
cout << "java" << endl;
}
};
class Python :public BasePage {
public:
void content() {
cout << "python" << endl;
}
};
class Cpp :public BasePage {
public:
void content() {
cout << "cpp" << endl;
}
};
void test01() {
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
Python py;
py.header();
py.footer();
py.left();
py.content();
Cpp cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
}
int main() {
test01();
system("pause");
return 0;
}
继承作用:可以减少重复代码
子类 也成为派生类
父类 也成为基类
继承语法: class 子类:继承方式 父类
继承方式一共有三种:
#include
using namespace std;
//继承方式
//公共继承
class Base1 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Base1 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son :public Base1 {
public:
void func() {
m_A = 10; //父类公共权限成员 到子类依然是公共权限
m_B = 10;//父类中的保护权限成员,到子类中依然是保护权限
//m_C = 10;//父类中私有成员 子类无法得到
}
};
void test01() {
Son s1;
s1.m_A = 100;
// s1.m_B = 100;//s1中m_B是保护权限 类外访问不到
}
// 保护继承
class Base2 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son2 :protected Base2 {
public:
void func() {
m_A = 10; //父类公共权限成员 到子类依然是保护权限
m_B = 10;//父类中的保护权限成员,到子类中依然是保护权限
//m_C = 10;//父类中私有成员 子类无法得到
}
};
void test02() {
Son2 s2;
//s2.m_A = 100; //在son2中m_A变成保护权限,因此类外访问不到
//s2.m_B = 100; //在son2中m_B变成保护权限,因此类外访问不到
}
//私有继承
class Son3 :private Base2 {
public:
void func() {
m_A = 100;
m_B = 100;
//m_C=100; //父类中私有成员,子类访问不到
}
};
void test03() {
Son3 s3;
//s3.m_A = 100; //在son2中m_A变成私有成员,因此类外访问不到
//s3.m_B = 100; //在son2中m_B变成私有成员,因此类外访问不到
}
class Grandson :public Son3 {
public:
void func() {
//m_A = 100; son3 m_A变为私有成员
}
};
int main() {
test01();
system("pause");
return 0;
}
#include
using namespace std;
//继承中的对象模型
class Base {
public:
int m_A;
private:
int m_B;
protected:
int m_C;
};
class Son :public Base {
public:
int m_D;
};
//利用开发人员命令提示工具查看对象模型
//查看命令 cl /d1 reportSingleClassLayout类名 文件名
void test01() {
//父类中所有非静态成员属性都会被子类继承下去
//父类中私有成员属性 是被编译器给隐藏了,因此访问不到,但是确实被继承下去了
cout << sizeof(Son) << endl; // 16
}
int main() {
test01();
system("pause");
return 0;
}
#include
using namespace std;
//继承构造和析构顺序
class Base {
public:
Base() {
cout << "Base构造函数" << endl;
}
~Base() {
cout << "Base析构函数" << endl;
}
};
class Son :public Base {
public:
Son() {
cout << "Son构造函数" << endl;
}
~Son() {
cout << "Son析构函数" << endl;
}
};
//Base构造函数
//Son构造函数
//Son析构函数
//Base析构函数
void test01() {
/*Base b1;*/
//继承中的构造和析构顺序如下:
//先构造父类,再构造子类,析构顺序与构造顺序相反
Son s1;
}
int main() {
test01();
system("pause");
return 0;
}
总结:先构造父类,再构造子类,析构顺序与构造顺序相反
#include
using namespace std;
//继承同名成员处理
class Base {
public:
Base() {
m_A = 100;
}
void func() {
cout << "Base" << endl;
}
void func(int a) {
cout << "Base"<<a << endl;
}
int m_A;
};
//同名成员属性调用
class Son :public Base {
public:
Son() {
m_A = 200;
}
void func() {
cout << "Son" << endl;
}
int m_A;
};
//同名成员函数调用
void test01() {
Son s;
cout << s.m_A << endl;
//如果通过子类对象 访问父类中同名成员,需要加作用域
cout << s.Base::m_A << endl;
s.func();
s.Base::func();
//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数
//如果项访问到父类中被隐藏的同名成员函数,需要加作用域
//s.func(100);
s.Base::func(1);
}
int main() {
test01();
system("pause");
return 0;
}
静态成员和非静态成员出现同名,处理方式一致
#include
using namespace std;
//继承中的同名静态成员处理方式
class Base {
public:
static int m_A;
static void func() {
cout << "Base" << endl;
}
};
int Base::m_A = 100;
class Son :public Base {
public:
static int m_A;
static void func() {
cout << "Son" << endl;
}
};
int Son::m_A = 200;
//同名静态成员属性
void test01() {
//1.通过对象访问
Son s;
cout << s.m_A << endl;
cout << s.Base::m_A << endl;
//2.通过类名方式访问
cout << Son::m_A << endl;
//第一个::代表通过类名方式访问 第二个::代表访问父类作用域下
cout << Son::Base::m_A << endl;
//通过对象访问
s.func();
s.Base::func();
//通过类名访问
Son::func();
Son::Base::func();
}
//同名静态成员函数
int main() {
test01();
system("pause");
return 0;
}
c++允许一个类继承多个类
语法: class 子类:继承方式1 父类1,继承方式2 父类2
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议多继承
#include
using namespace std;
//继承中的同名静态成员处理方式
class Base1 {
public:
Base1() {
m_A = 100;
}
int m_A;
};
class Base2 {
public:
Base2() {
m_A = 200;
}
int m_A;
};
class Son :public Base1, public Base2 {
public:
Son() {
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test01() {
Son s;
cout << sizeof(s) << endl;
//当父类中出现同名成员,需要加作用域区分
//cout << s.m_A << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
//同名静态成员函数
int main() {
test01();
system("pause");
return 0;
}
总结:多继承中如果父类中出现了同名情况,子类使用时要加作用域
菱形继承概念:
#include
using namespace std;
//动物类
class Animal {
public:
int m_Age;
};
//利用虚继承 解决菱形继承问题
// 继承之前 加上关键字 virtual 变为虚继承
// Animal类称为虚基类
//羊类
class Sheep:virtual public Animal{};
//驼类
class Tuo:virtual public Animal{};
//羊驼类
class Yangtuo :public Sheep, public Tuo{};
void test01() {
Yangtuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 29;
//当菱形继承时,两个父类拥有相同数据,需要加以作用域区分
cout << st.Sheep::m_Age << endl;
cout << st.Tuo::m_Age << endl;
//这份数据一个即可,菱形继承导致数据两份,资源浪费
cout << st.m_Age << endl;
cout << sizeof(st) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
多态是C++面向对象三大特性之一
多态分为两类:
静态多态和动态多态区别:
#include
using namespace std;
//多态
//动物类
class Animal {
public:
//虚函数
virtual void speak() {
cout << "动物在说话" << endl;
}
};
class Cat :public Animal {
public:
//重写 函数返回值类型 函数名 函数列表 要相同
void speak() {
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal {
public:
void speak() {
cout << "小狗在说话" << endl;
}
};
//地址早绑定 在编译阶段确定函数地址
//如果执行猫说话,那么这个函数地址不能提前绑定,需要在运行阶段绑定,地址晚绑定
//动态多态满足条件
//1.有继承关系
//2.重写父类虚函数
//动态多态使用
//父类指针或者引用 指向与类对象
void doSpeak(Animal& animal) { //Animal &animal=cat
animal.speak();
}
void test01() {
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
int main() {
test01();
system("pause");
return 0;
}
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
语法: virtual 返回值类型 函数名 (参数列表)=0;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
#include
using namespace std;
//纯虚函数和抽象类
class Base {
public:
//纯虚函数
//只要右一个纯虚函数,这个类称为抽象类
//抽象类特点:
//1.无法实例化对象
//2.抽象类的子类 必须要重写父类中的纯虚函数 否则也属于抽象类
virtual void func() = 0;
};
class Son :Base {
public:
void func() {
}
};
void test01() {
//Base b;//抽象类是无法实例化对象
//new Base;
Son s; // 子类必须重写父类中纯虚函数
}
int main() {
test01();
system("pause");
return 0;
}
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
虚析构和纯虚析构区别:
虚析构语法: virtual ~类名(){}
纯虚析构语法:virtual ~类名()=0;
#include
using namespace std;
//虚析构和纯虚析构
class Animal {
public:
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 name) {
cout << "cat构造函数调用" << endl;
m_Name=new string(name);
}
virtual void speak() {
cout << *m_Name<<"小猫在说话" << endl;
}
~Cat() {
if (m_Name != NULL) {
cout << "cat析构函数调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
string *m_Name;
};
void test01() {
Animal* animal = new Cat("TOM");
animal->speak();
//父类指针在析构时候,不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
delete animal;
}
int main() {
test01();
system("pause");
return 0;
}
C++中对文件操作需要包含头文件
文件类型分为两种:
操作文件的三大类:
写文件步骤如下:
1.包含头文件
#incluede
2.创建流对象
ofstream ofs;
3.打开文件
ofs.open(“文件路径”,打开方式);
4.写数据
ofs<<“写入数据”
5.关闭文件
#include
#include //包含头文件
using namespace std;
//文本文件 写文件
void test01() {
//1.包含头文件 fstream
//2.创建流对象
ofstream ofs;
//3.指定打开方式
ofs.open("test.txt", ios::out);
//4.写数据
ofs << "张三" << endl;
ofs << "男" << endl;
//5.关闭文件
ofs.close();
}
int main() {
test01();
system("pause");
return 0;
}
步骤类似,但是读取方式相对于比较多
读文件步骤如下:
1.包含头文件
#inclued
2.创建流对象
ifstream ifs;
3.打开文件并判断文件是否打开成功
ifs.open(“文件路径”,打开方式);
4.读数据
四种读取方式
5.关闭文件
ifs.close();
#include
#include //包含头文件
#include
using namespace std;
//文本文件 读文件
void test01() {
//1.包含头文件 fstream
//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) { //EOF end of file
cout << c;
}
//5.关闭文件
ifs.close();
}
int main() {
test01();
system("pause");
return 0;
}
以二进制方式对文件进行读写操作
打开方式要指定为ios::binary
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数
#include
#include //包含头文件
#include
using namespace std;
//二进制文件 写文件
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);*/
//4.写文件
Person p = { "张三",18 };
ofs.write((const char*)&p, sizeof(Person));
//5.关闭文件
ofs.close();
}
int main() {
test01();
system("pause");
return 0;
}
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存种一段存储空间,len是读写的字节数
#include
#include //包含头文件
#include
using namespace std;
//二进制文件 读文件
class Person {
public:
char m_Name[64]; //姓名
int m_Age;//奈年龄
};
void test01() {
//1.包含头文件
//2.创建流对象
ifstream ifs;
//3.打开文件 判读是否打开成功
ifs.open("person.txt", ios::out | ios::binary);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
//4.写文件
Person p;
ifs.read((char *) & p,sizeof(Person));
cout << p.m_Name << p.m_Age << endl;
//5.关闭文件
ifs.close();
}
int main() {
test01();
system("pause");
return 0;
}