Noname's Blog
信息安全专业的小萌新,立志走上更大的舞台
快速食用C++

快速食用C++

相当于笔记吧,学习时候学到的基础知识应该都往这里塞了
会包含一些课堂笔记、自主学习笔记、读书笔记
毕竟不好好看ppt考试好容易吃亏啊- -
安装VS2017,记录一些快捷键

Ctrl + Enter = 在当前行插入空行
Ctrl + Shift + Enter = 在当前行下方插入空行
Ctrl + 空格键 = 使用 IntelliSense(智能感知)自动完成
Alt + Shift + 箭头键(←,↑,↓,→) = 选择代码的自定义部分
Ctrl + } = 匹配大括号、括号
Ctrl + Shift +} = 在匹配的括号、括号内选择文本
Ctrl + Shift + S = 保存所有文件和项目
Ctrl + K,Ctrl + C = 注释选定行
Ctrl + K,Ctrl + U = 取消选定行的注释
Ctrl + K,Ctrl + D = 正确对齐所有代码
Shift + End = 从头到尾选择整行
Shift + Home = 从尾到头选择整行
Ctrl + Delete = 删除光标右侧的所有字

格式化输出

cout对象提供带格式的数据输出,格式化输出的操作符主要又setw、setprecision、setiosflags

stew

stew用于指定输出数据项的域宽(需包含头文件iomanip)
向右对对齐,空格补在前面

int num1 = 1234;
float num2 = 1.22
cout << "<" << setw(5) << num1 << ")" << endl;
cout << "<" << setw(5) << num2 << ")" << endl;

setprecision

percision 有效位数 默认有效位数是6位

float x = 1.2345f;
cout << setprecision(9) << x << endl;
cout << setprecision(3) << x << endl;
cout << setprecision(2) << x << endl;
cout << setprecision(1) << x << endl;

注意:setprecision精度的设置在重新设置之前一直有效,setw仅仅对与其相邻一个输出项有效

setiosflags

用来控制cout输出定点形式的浮点数
注意于setprecision的连用

cout << setprecision(2) << setiosflags(ios::fixed);//此时setprecision是指保留两位小数位
//因为setprecision一直有效,所以新的设置不能覆盖前面的设置

设置和取消

cout.self(ios::fixed | ios::left)
cout.unself(ios::fixed | ios::left)

表:操作符setiosflags状态标志位的功能

状态标志功能
ios::left左对齐,右边填充空格
ios::right右对齐,左边填充空格
ios::fixed以定点形式输出浮点数(小数点在数中的位置固定)
ios::scientific以科学计数法形式输出浮点数
ios::dec使随后的所有整数以十进制形式输出
ios::hex使随后的所有整数以十六进制形式输出
ios::oct使随后的所有整数以八进制形式输出
ios::showpoint输出小数点和尾部的零
ios::showpos在正数前面输出+
ios::uppercase对于十六进制输出,使用大写字母表示

采用函数成员实现格式化输出

通过cout的成员函数来修改

float x = 12.345f;
cout.width(5);
cout.precision(3);
cout << x;
//cout << setw(5) << setprecision(3) << x;

格式化输入

cin对象可以用来控制字符或者字符串的输入,遇到空字符时将停止读入

char world[10];
cin.width(10);//最多读入9个字符
cin >> world;//相当于下面
cin >> setw(10) >> world;

即cin可以跳过前面的空白字符

char ch;

cout << "输入一个字符并回车:";
cin >> ch;//输入"                 a"
cout << "你输入的是:" << ch << endl;//a

当遇到后面的空白字符时将停止读入,且读取一定的字符以后,多余的字符将留在缓冲区
空白字符:回车、空格、Tab

getline

读取一行

char str[81];

cout << "please input a line" << endl;
cin.getline(line, 81);

cin.get

可以读取包含空字符在内的任意字符
可以用于实现暂停

char ch;
cout << "Please input: ";
cin.get(ch);//回车

cin.ignore

能够使得cin对象跳过键盘缓冲区字符

cin.ignore(20, '\n');
//cin 跳过后面20个字符
//或者直至开始一个新行

cin.ignore();//跳过下一个字符

注意

混合使用cin>>和cin.get

cout << "Please input a number: ";
cin >> number;
//cin.ignore();//添加后可以解决
cout << "Please input a char";
cin.get(ch);//把上一个数字输完后点的回车键吃进去了

题目:

#include<iostream>
using namespace std;

int main()
{
	char ch;
	cout << "Please input:" << endl;
	cin >> ch; //输入10
	cout << ch; //得到1
	return 0;
} 

cin/cout自动匹配数据类型,输入10会当做输入了一个字符1和字符0,字符1显示到黑框框,0保留在缓冲区

引用

语法: 数据类型 &别名 = 原名

int a = 100;
int &b = a;

cout << a << endl;//100
cout << b << endl;//100

b = 200;

cout << a << endl;//200
cout << b << endl;//200

注意:引用必须初始化,且初始化后不能再改变

int a = 10;
int b = 20;
//int &c;//不妥
int &c = a;
c = b;//相当于一种赋值操作,将b的值赋给c指向的区域

cout << a << endl;//20
cout << b << endl;//20
cout << c << endl;//20

引用做函数参数

函数传参时,可以利用引用的让形参修饰实参
优点;简化指针修改实参

//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;

	Swap01(a, b);
	cout << "a:" << a << " b:" << b << endl;

	Swap02(&a, &b);
	cout << "a:" << a << " b:" << b << endl;

	Swap03(a, b);
	cout << "a:" << a << " b:" << b << endl;

	system("pause");

	return 0;
}

引用做函数返回值

注意:不要返回局部变量

//返回局部变量引用
int & test01()
{
    int a = 10;//局部变量
    return a;
}

//返回静态变量引用
int & test02()
{
    static int a = 20;
    return a;
}

int main()
{
    //不能返回局部变量的引用
    int &ret1 = test01();
    cout << "ret:" << ret1 << endl;
    cout << "ret:" << ret1 << endl;
    
    
    //若函数做左值,那么必须返回引用
    int &ref2 = test02();
    cout << "ret:" << ret2 << endl;
    cout << "ret:" << ret2 << endl;

    test02() = 100;
    cout << "ret:" << ret2 << endl;
    cout << "ret:" << ret2 << endl;
    
    system("pause");
    return 0;
}

引用的本质

本质是c++内部实现的一个指针常量(指向不能更改)

引用与指针的区别

引用是变量别名,不占空间
指针占空间,且可以指向不同的变量,可以修改和释放

常量引用

作用:修饰形参,防止误操作

void func(const int& v)
{
    //val += 10;//改是不能改了
    cout << val << endl;
}

int main()
{
    //int& ret = 10;//这种不妥
    
    const int& ret = 10;
    
    //ret = 100;//const之后不能修改变量
    cout << ret << endl;
    //函数中利用常量引用防止误操作修改实参
    int a = 10;
    func(a);
    
    system("pause");
    return 0;
}

c++中的函数扩展部分

函数的默认(缺省)参数

语法:

返回值类型 函数名 (参数=默认值){}
int add(int x, int y=5, int z=6);
add(4);//15
add(4, 10);//20 
add(4, 10, 16);//30

1.缺省形参值必须从右向左顺序声明,并且在缺省形参值的右面不能有非缺省形参值的参数。(因为调用时实参取代形参时从左向右)

int add(int x, int y=5, int z=6);//正确
int add(int x=1, int y=5, int z);//错误
int add(int x=1, int y, int z=6);//错误

2.若函数声明有默认值,函数实现的时候不能有默认参数

函数占位参数

语法:

返回值类型 函数名(数据类型){}
void func(int a, int )
{
    cout << "func" << endl;
}

func(10, 10);//占位参数也必须填补

函数重载

内存分区模型

代码区:存放函数体的二进制代码,由操作系统进行管理
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
堆区:由程序员分配和释放,若程序员不进行释放操作,程序结束时会由操作系统进行回收

代码区

存放CPU执行的二进制机器指令
共享、只读

全局区

全局变量、静态变量
常量区、字符串常量、其他常量(const修饰的变量)
该区域的数据在程序结束后由操作系统释放(程序员不控制其生命周期)
注意:局部常量

栈区

栈区的数据由编译器管理开辟和释放,且在函数执行完后自动释放

int * func()
{
    int a = 10;//局部变量
    return &a;//返回了局部变量的地址
}

int main()
{
    int *p = func();
    
    cout << *p << endl;
    cout << *p << endl;//乱码 
    
    system("pause");
    return 0;

}

注意:形参也在栈上

堆区

堆区由程序员分配和释放,或是由程序结束时由操作系统回收

new操作符

语法

new 数据类型(大小)

开辟一块堆区域

new int(10);

开辟数组

new int[10];

delete

手动释放堆区数据

int * func()
{
    int* a = new int(10);//拿指针接收,指针也是栈区的变量,存下的是指向堆区的那一块区域的地址
    return a;
}

int main()
{
    int *p = func();
    
    cout << *p << endl;
    cout << *p << endl;//不会被自动释放
    
    delete p;//手动释放
    
    //释放后不可再访问了
    
    system("pause");
    return 0;
}

类和对象

万物皆对象!封装!继承!多态!

封装

将一些事物相同的属性和行为抽象成一个类
将属性和行为加以权限控制

类的语法:
class 类名(访问权限 : 属性/行为);

访问权限:
public 公共权限 类内类外可以访问
protected 保护权限 类内可以方位,类外不可以访问
private 私有权限 类内可以访问,类外不可以访问

class Person
{
public:
    string m_Name;
    
protected:
    int m_Age;
    
private:
    int m_Password;


public:
    void func()
    {
       //具体实现
    }
};

int main()
{
    Person p;//实例化一个对象
    p.m_Name;//可以直接访问公共权限的属性
    p.func();//可以访问公共权限的方法
    //p.m_Age;//访问不到
    //p.m_Password;//访问不到
    return 0;
}

将成员属性设置为私有,可以控制读写权限(提供对外的getset函数)

class Person
{
public:
    //读取姓名
    string getName()
    {
        return m_Name;
    }
    
    //设置姓名
    void setName(string name)
    {
        m_Name = name;
    }
    
    //获取年龄
    int getAge(){
        return m_Age;
    }
    
    //设置姓名
    void setAge(int age){
        //对这种变量最好做一个判断
        if(age < 0 || age > 200)
        {
            cout << "年龄设置错误" << endl;
            return 0;
        }
        m_Age = age;
    }
    
private:
    string m_Name;//可读可写
    
    int m_Age;//只读
    
    string m_gender;//只写

}

int main()
{
    Person p;//实例化一个对象
    p.setName("小c");
    
    system("pause");
    return 0;
}

struct和class的区别

默认访问权限不同
struct 默认权限为公共
class 默认权限为私有

构造函数和析构函数相关

作用:初始化和清理对象
构造函数: 主要作用在创建对象时,为对象的成员属性赋值,由编译器自动调用
语法

类名(){}

特点

  1. 无返回值,也无需写void
  2. 名称与类名相同
  3. 可以有参数,因此可以发生重载
  4. 程序在调用对象时会自动调用构造函数,且只会调用一次

析构函数:主要作用在对象销毁前系统自动调用,执行清理工作
语法

~类名(){}

特点

  1. 没有返回值也不写void
  2. 函数名称是与类名相同且在名称前加上~
  3. 不可以有参数,不可以发生重载
  4. 程序在对象销毁前会自动调用析构函数,且只会调用一次

构造函数的分类及调用

按参数:有参构造和无参构造(默认构造函数)
按类型:普通构造和拷贝构造

三种调用方式:括号法、显示法、隐式转换法

class Person
{
public:
    Person()
    {
        cout << "Person的无参构造函数" << endl;
    }
    
    Person(int age)
    {
        m_Age = age;
        cout << "Person的有参构造函数" << endl;
    }
    
    Person(const Person &p)
    {
        m_Age = p.m_Age;
        cout << "Person的拷贝构造函数" << endl;
    }

    ~Person()
    {
        cout << "Person的析构函数" << endl;
    }
public:
    int m_Age;
};

void test01()
{
    Person p1;//注意调用无参构造不能加括号
    //Person p();//编译器会认为是一个函数声明,还挺像
}

void test02()
{
    //括号法
    Person p2(10);
    //显示法
    Person p3 = Person(10);
    Person p4 = Person(p3);//拷贝了一个p3进p4
    
    //Person(10);//匿名对象,当前行结束之后,马上析构
    
    //隐式转换法
    Person p5 = 10;//Person p5 = Person(10);
    Person p6 = p5;//Person p6 = Person(p4);
    
    //不能利用拷贝函数初始化匿名对象
    //Person p6(p5);//编译器会认为是对象声明
}

调用规则

默认情况下,c编译器至少给一个类添加3个函数
默认构造函数(无参,空实现)+默认析构函数(无参,空实现)
若用户定义了有参构造函数,c
只会提供默认拷贝构造函数,不再提供默认无参构造函数
若用户定义了拷贝构造函数,c++不会再提供其他构造函数
做个实验就知道了~

深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作,默认拷贝构造函数就是
深拷贝:在堆区重新申请空间,进行拷贝操作

若使用了默认拷贝构造函数,将p1复制给p2,最后析构时候会发生重复释放内存

给拷贝的对象也开辟一块内存区域,使得两个对象释放内存时互不干扰

所以如果有属性在堆区开辟,要自己实现拷贝构造函数

class Person
{
public:
    Person()
    {
        //可以输出体现一下
    }
    
    Person(int age, int height)
    {
        m_age = age;
        //m.Height = height;
        m_height = new int(height);//在堆区开辟一个新区域
    }
    Person(const Person &p)
    {
        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;
        }
    }
    
    int m_Age;
    int * m_Height;//用一个指针指向堆区的一块区域
};

//访问的时候要记得解引用熬

初始化列表

也是一个新事物!
语法:

构造函数():属性1(值1),属性2(值2)... {}
//一款传统的写法
Person(int a, int b, int c)
{
    m_A = a;
    m_B = b;
    m_C = c;
}
//一款新潮却有一些缺陷的写法
Person():m_A(10), m_B(20), m_C(30){}
//一款新潮又能改值的写法
Person(int a, int b, int c):m_A(a), m_B(b), m_C(c){};

类对象作为类成员--谁先出生谁先die?

class A{}
class B
{
    A a;
}

创建B对象
分析构造和析构的顺序:

class A
{
public:
    A()
    {
        cout << "A的构造函数" << endl;
    }
    ~A()
    {
        cout << "A的析构函数" << endl;
    }
    int a_Age = 10;
};

class B
{
public:
    B()
    {
        cout << "B的构造函数" << endl;
    }
    ~B()
    {
        cout << "B的析构函数" << endl;
    }
    
    void func()
    {
        cout << "B和A是好朋友,B " << b_Age << "岁了, A " << a.a_Age << "岁了" << endl;
    }
    
    int b_Age = 20;
    A a;
};

void test01()
{
    B b;
    b.func();
}

运行结果

A的构造函数
B的构造函数
B和A是好朋友,B 20岁了, A 10岁了
B的析构函数
A的析构函数

先调用对象成员的构造,再调用本类构造,析构顺序与构造顺序相反

静态成员

静态成员变量
1.所有对象都共享同一份数据
2.编译阶段就分配内存
3.类内声明,类外初始化

class Person
{
public:
    
    static int m_A;
    
}
int Person::m_A = 100;
void test01()
{
    Person p1;
    cout << p.m_A << endl;//如果没有进行初始化->无法解析的外部命令,一般是链接阶段出现的错误
    Person p2;
    p2.m_A = 200;
    
    cout << p1.m_A << endl;//被改了
}

静态成员变量不属于某个对象,所有对象都共享一份数据,所以上方代码中无论通过p1还是p2改变了m_A的值,都是改变的同一份m_A

访问静态成员变量的两种方式
1.通过对象进行访问

Person p;
cout << p.m_A;

2.通过类名进行访问

cout << Person::m_A;

注意:静态成员也有访问权限的问题,私有的静态成员变量也是不能直接被外界访问的

C++对象模型和this指针

略略略

友元

程序中会有些私有属性希望让类外特殊的
一些函数或者类访问到,所以需要友元,将访问我们类的那些东西附上"好朋友"属性
--- 让一个函数/类访问另一个类种的私有成员

全局函数做友元

故事:从前有一个龚憨憨,她想进胡憨憨(也就是上文说的我儿子)的家,但是胡憨憨部让龚憨憨进房间,此时龚憨憨企图进入胡憨憨的房间

这是胡憨憨的家

class Home
{
public:
        Home()
        {
               m_SittingRoom = "龚憨憨可用进来的地方";
               m_Bedroom = "胡憨憨不给龚憨憨进来的地方";
        }
public:
        string m_SittingRoom;
private:

        string m_BedRoom;
};

//龚憨憨开始出动了
void gll_Visit(Home *h)
{
        cout << "gll正在踏入:" << h->m_SittingRoom << endl;
        //cout << "gll正在踏入:" << h->m_Bedroom << endl;//此时会报错
}
//龚憨憨试验
void test01()
{
        Home h;//胡憨憨的家
        gll_Visit(&h);//gll串门
}

fail.png

龚憨憨如今只能如此
她偷偷在代码里加了这么一行

friend void gll_Visit(Home *H);

此时放开这段代码

cout << "gll正在踏入:" << h->m_Bedroom << endl;

sus.png
龚憨憨成功了(臭变态

类做友元

此时,龚憨憨带领了一群朋友(class Person)想进入胡憨憨的房间

class Person
{
public:
        Person();//要初始化home指针
        void visit();
private:
        Home *home;
};

class Home
{
        friend class Person;//从此这群人就拥有了鸡喙
public:
        Home();
public:
        string m_SittingRoom;
private:
        string m_BedRoom;
};

成员函数做友元

friend void Person::visit();//那就除了看啥都不能干呗

继承

面向对象三大特性之一!
有一个特别重要的理解就是 公共!
在这里开始认识爹和儿子,也就是 遗传!

基本语法:
class 子类 : 继承方式 父类;
(派生类) (基类)

继承方式:

公共继承 
保护继承
私有继承

一个实验证明继承方式能访问到哪些权限

class Base
{
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son1 : public Base
{
public:
    void func()
    {
        m_A;
        m_B;
        //m_C;
    }
};

void test01()
{
    Son1 s1;
    s1.m_A;//类外只能访问到公共权限
}
//保护继承
class Son2 : protected Base
{
public:
    void func()
    {
        m_A;
        m_B;
        //m_C;
    }
};

void test02()
{
    Son2 s2;
    //s2.m_A;
}

//私有继承
class Son3 : private Base
{
public:
    void func()
    {
        m_A;
        m_B;
        //m_C;
    }
};

//注意这个孙子!
class GrandSon3 : public Son3
{
public:
    void func()
    {
        //因为Son3是私有继承,所以这个孙子无法访问到Son3种的所有属性
    }
}

继承中的对象模型

派生类中包含的是继承到的部分,和自己的部分
父类中的私有成员也被子类继承,但是被编译器隐藏后无法访问
利用VS的开发人员命令提示符工具
定位到当前cpp文件的盘符->

cl /d1 reportSingleClassLayout类名 所属文件名

c字母,d一 所属文件名可以按tab自动补全

继承中构造和析构顺序

子类继承父类,当创建子类对象的时候也会调用父类的构造函数。
实验分析父类和子类的构造和析构顺序

class Base
{
public:
    Base()
    {
        cout << "Base构造函数" << endl;
    }
    ~Base()
    {
        cout << "Base析构函数" << endl;
    }
};
class Son : public Base
{
public:
    Son()
    {
        cout << "Son构造函数" << endl;
    }
    ~Son()
    {
        cout << "Son析构函数" << endl;
    }
}
void test()
{
    Son s;
}

继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

继承同名成员处理方式

处理子类和父类出现同名的成员,通过子类对象,访问到子类或父类中同名的数据
访问子类同名成员->直接访问
访问父类同名成员->加作用域

Son s;
s.Base::func();
s.Base::func(10);

注意:当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数,包括各种有参无参

继承同名静态成员处理方式

静态成员处理方式相同
访问同名静态成员属性:

//通过对象访问
Son s;
s.m_A;
s.Base::m_A;
//通过类名访问
Son::m_A;
Son::Base::m_A;

访问同名静态成员函数

//通过对象访问
Son s;
s.func();
s.Base::func();
//通过类名访问
Son::func();
Son::Base::func();;

多继承

class 子类 : 继承方式 父类1, 继承方式 父类2...

注意同名的情况,子类用作用域区分

菱形继承

概念:当两个派生类B、C继承同一个基类A,又有某个类D同时继承两个派生类B和C
会出现的问题:
B和C继承了A的数据,当D使用数据时会产生二义性(继承了两份来自A的数据)
QQ截图20200226120124.png

解决方法:
虚继承

class B : virtual public A{};
class C : virtual public A{};

QQ截图20200226120020.png

多态

静态多态:函数重载和运算符重载属于静态多态,复用函数名 -- 函数地址早绑定-编译阶段确定函数地址
动态多态:派生类和虚函数实现运行时多态 -- 函数地址晚绑定-运行阶段确定函数地址

class Base
{
public:
    void func(){};
    
    virtual void func(){};
};

class Son1 : public Base
{
public:
    void func()
    {
        cout << "func1" << endl;    
    };
};

class Son2 : public Base
{
public:
    void func()
    {
        cout << "func2" << endl;    
    };
};

void doFunc(Base &base)
{
    base.func();
}

void doFunc2(Base *base)
{
    base->func();
    delete base;
}

void test01()
{
    Son1 s1;
    doFunc(s1);
    
    Son2 s2;
    doFunc(s2);
}

int main()
{
    test01();

    system("pause");
    return 0;
}

多态满足条件:
1.有继承关系
2.子类重写父类中的虚函数
多态使用条件:
父类指针或引用指向子类对象(所以是有两种方式)

重写:函数返回值类型、函数名、参数列表完全一致

纯虚函数和抽象类

纯虚函数的类成为抽象类

virtual 返回值类型 函数名(参数列表) = 0;

抽象类的特点:

无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类

class Base
{
public:
    virtual void func() = 0;
};

class Son : public Base
{
public:
    virtual void func()
    {
        cout << "func调用" << endl;
    }
};

void test01()
{
    Base *base = NULL;
    //base = new Base;//万万不可实例化对象
    base = new Son;
    base->func();
    delete base;
}

虚析构和纯虚析构

多态使用时,若子类中有属性是开辟在堆区的,那么父类指针在释放时无法调用到子类的析构代码,所以需要将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构的共同点:

解决父类指针释放子类对象
都需要有具体的函数实现

虚析构语法

virtual ~类名(){}

纯虚析构语法

virtual ~类名(){}
类名::~类名(){}

注意:包含纯虚析构函数的类也是抽象类,不能被实例化

class Base
{
public:
    Base()
    {
        cout << "Base的构造函数" << endl;
    }
    virtual void speak() = 0;
    virtual ~Base() = 0;
};
Base::~Base()
{ cout << "Base纯虚析构函数" << endl;}

class Son : public Base
{
public:
    Son(string name)
    {
        cout << "Son构造函数调用" << endl;
        m_Name = new string(name);
    }
    void speak()
    {
        cout << "儿子在嘤嘤嘤" << endl;
    }
    ~Son()
    {
        cout << "Son析构函数调用" << endl;
    }
    string *m_Name;
};

void test()
{
    Base *b = new Son("胡憨憨");
    b->speak();
    delete b;//用虚析构/纯虚析构来解决通过父类指针释放子类对象(彻底清理子类对象,防止内存泄露)
}
Base的构造函数
Son构造函数调用
儿子在嘤嘤嘤
Son析构函数调用
Base纯虚析构函数

若没有采用虚析构/纯虚析构

Base的构造函数
Son构造函数调用
儿子在嘤嘤嘤
Base的析构函数

文件操作

要包含头文件
iostream 写操作
ifstream 读操作
fstream 读写操作

文件打开方式:
| 打开方式 | 解释 |
| ----------- | -------------------------- |
| ios::in | 为读文件而打开文件 |
| ios::out | 为写文件而打开文件 |
| ios::ate | 初始位置:文件尾 |
| ios::app | 追加方式写文件 |
| ios::trunc | 如果文件存在先删除,再创建 |
| ios::binary | 二进制方式 |
注:可用利用 | 操作符配合使用

写文本文件的步骤
1.包含 fstream
2.创建流对象 ofstream ofs
3.打开文件 ofs.open("文件路径",打开方式);
4.写数据 ofs<<"写入的数据"
5.关闭文件 ofs.close()

#include<fstream>

void test01()
{
    ofstream ofs;
    ofs.open("test.txt", ios::out);
    
    ofs << "姓名:ccc" << endl;
    
    ofs.close();
}

读文本文件步骤:
1.包含头文件
2.创建流对象 ifstream ifs;
3.打开文件并判断文件是否打开成功 ifs.open();
4.读数据
5.关闭文件

读数据的四种方式
第一种

char buf[1024] = { 0 };//初始化一个缓冲区
while(ifs >> buf)
{
    cout << buf << endl;
}

第二种

char buf[1024] = { 0 };
while(ifs.getline(buf, sizeof(buf)))
{
    cout << buf << endl;
}

第三种

string buf;
while(getline(ifs, buf))
{
    cout << buf << endl;
}

第四种

char c;
while((c = ifs.get()) != EOF)
{
    cout << c;
}
#include<fstream>
#include<string>
void test01()
{
    ifstream ifs;
    ifs.open("test.txt", ios::in);
    
    if(!ifs.is_open()) //判断如果文件没有成功打开
    {
        cout << "文件打开失败" << endl;
        return;
    }
    
    char c;
    while((c = ifs.get()) != EOF)
    {
        cout << c;
    }
    
    ifs.close();
}

二进制文件

打开方式要指定为 ios::binary

写二进制文件

主要利用流对象调用成员函数write
函数原形:

ostream& write(const char * buffer,int len);

字符指针buffer指向内存中一段存储空间,len是读写的字节数

//1.包含头文件
#include <fstream>
#include <string>

class Person
{
public:
    char m_Name[64];
    int m_Age;
};

void test01()
{
    //2.创建输出流对象
    ofstream ofs("person.txt", ios::out | ios::binary);
    //3.打开文件   
    ofs.open("peson.txt", ios::out | ios::binary);
    
    Person p = {"ccc", 18};
    //4.写文件
    ofs.write((const char *)&p, sizeof(p));
    //5.关闭文件
    ofs.close();
}

读二进制文件

主要利用流对象调用成员函数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()
{
    ifstream ifs("person.txt", ios::in | ios::binary);
    if(!ifs.is_open())
    {
        cout << "文件打开失败" << endl;
    }
    
    Person p;
    ifs.read((char * )&p, sizeof(p));//&p会返回Person类型,所以需要墙砖
    
    cout << "姓名:" << p.m_Name << "年龄:" << p.m_Age << endl;
}