当前位置: 首页 > article >正文

C++中级学习笔记

1.内存分区模型:

C++程序在执行时,将内存大方向划分为四个区域

(1)代码区:存放函数体的二进制代码,由操作系统进行管理

(2)全局区:存放全局变量和静态变量以及变量

(3)栈区:由编译器自动分配释放,存放函数的参数值,局部变量等

(4)堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

四区的意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程

在程序运行前:

  生成了exe可执行程序,未执行该程序前分为两个区域

  代码区:

         存放CPU执行的机器指令

         代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

         代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

  全局区:

         全局变量和静态变量存放在此

         全局区还包含了常量区,字符串常量和其他常量也存放在此

         该区域的数据在程序结束后由操作系统释放 

  栈区:

          由编译器自动分配释放,存放函数的参数值,局部变量等

          注意事项:不要返回局部变量的地址

          形参数据也会放在栈区

  堆区:

          由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

          在C++中主要利用new在堆区开辟数据

#include<iostream>
using namespace std;

int* func()
{
	//利用new关键字,将数据开辟到堆区
	int* p = new int(10);
	return p;
}
//这里函数中的p被释放之前,把地址传给了main中的P,以访问堆区的10
int main()
{
	//在堆区开辟数据
	int* p = func();
	//10 10 地址
	cout << *p << endl;
	cout << *p << endl;
	cout << &p << endl;

	system("pause");

	return 0;
}

  new操作符:

#include<iostream>
using namespace std;

int* func()
{
	//在堆区创建整型数据
	//new返回的是 该数据类型的指针
	int* p = new int(10);
	return p;
}
void test01()
{
	int* p = func();
	cout << *p << endl;
	cout << *p << endl;
	cout << *p << endl;
	//堆区的数据由程序员管理
	//利用关键字delete释放
	delete p;

	//cout << *p << endl;该操作会报错,内存已经被释放
}
//利用new开辟堆区的数组
void test02()
{
	//创建10个整型的数组,返回数组的首元素地址
	int *arr =new int[10];
	//arr就变成了数组名
	arr[1] = 1;
	//释放数组需要加一个中括号
	delete[]arr;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

2.引用

基本用法:

#include<iostream>
using namespace std;
//引用:给变量起别名
//语法:数据类型 &别名 = 原名;(数据类型得一样)
int main()
{
	int a = 10;
	int& b = a;
	cout << a << endl;
	cout << b << endl;
	b = 20;
	cout << a << endl;
	cout << b << endl;
	system("pause");
	return 0;
}

 注意事项:

(1)引用必须初始化

(2)引用一旦初始化后,就不可以更改

引用作函数传递参数:

#include<iostream>
using namespace std;
void func(int& a, int& b)
{
	int num = a;
	a = b;
	b = num;
}
int main()
{
	int a = 0;
	int b = 10;
	func(a, b);//引用传递,形参会修饰实参
	cout << a << endl;
	cout << b << endl;
	system("pause");
	return 0;
}

引用做函数返回值:

#include<iostream>
using namespace std;
//引用作函数返回值
//1.不要返回局部变量的引用
int& test01()
{
	static int a = 10;//静态变量放在全局区
	return a;
}
int main()
{
	int &num = test01();
	cout << num << endl;
	cout << num << endl;
	test01() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值
	cout << num << endl;
	cout << num << endl;
	system("pause");
	return 0;
}

引用的本质:

引用的本质是一个指针常量

常量引用:

#include<iostream>
using namespace std;
//加上const之后,值不可改,避免出现错误
void showNum(const int& a)
{
	cout << a << endl;
}
int main()
{
	//常量引用
	//使用场景:用来修饰形参,防止误操作
	//加上const之后,编译器将代码修改 int temp = 10;const int & ref =temp;
	//const int& ref = 10;

	int a = 1000;
	showNum(a);
	system("pause");
	return 0;
}

函数默认参数: 

#include<iostream>
using namespace std;

//函数默认参数

//int func(int a, int b, int c)可以这样写
//也可以下面这样写
//注意事项:
//如果在函数定义实现处写出默认值,就可以在main中省略后两个值的输入
//声明和实现处只能有一处默认值
//在函数内部不能再赋予其其他默认值
//语法: 返回值类型 函数名 (形参=默认值){}
int func(int a, int b=20, int c=30)
{
	return a + b + c;
}

int main()
{
	cout << func(10, 20, 30) << endl;
	system("pause");
	return 0;
}

函数占位参数:

//函数占位参数
//返回值类型  函数名(数据类型,占位){}
//下面这个函数,使用int占在那里int还可以有默认值
void func(int a, int=10)
{
	cout << "this is func" << endl;

}
int main()
{
	func(10, 10);
	system("pause");
	return 0;
}

函数重载:

//函数重载
//作用:函数名可以相同,提高复用性 
//函数重载满足条件:1 同一个作用域下 2 函数名称相同 3 函数参数类型不同 或者 个数不同 或者顺序不同
//函数的返回值不可以当作函数重载的条件
void func(long int b,int a)
{
	cout << "func的调用" << endl;
}

void func(int a)
{
	cout << "func" << endl;
}
void func(long int a)
{
	cout << "func" << endl;
}
void func(int a,long int b)
{
	cout << "func" << endl;
}
int main()
{
	func(10);
	system("pause");
	return 0;
}

函数重载注意事项:

//1.引用作为函数重载的条件
//下面这两个这样写是被允许的 const也算不同类型的一种
void func(int& a)
{

}
void func(const int& a)
{

}
int main()
{
	//这样会调用上面的函数
	int a = 10;
	func(a);
	//这样会调用下面的参数
	//直接传入10,上面的函数不合法,传入下面的函数含有const,编译器会开辟出int t =10  int &a=t来使其合法
	func(10);
	system("pause");
	return 0;
}
//2.函数重载遇到默认参数
void func(int a,int b=10)
{

}
void func(int a)
{

}
int main()
{
	func(10);//这样会出现歧义,无法重载
	return 0;
}

类和对象

c++面向对象的三个特性:封装 继承 多态   万事万物都可以作为对象,对象上有其属性和行为 

设计一个圆类,来计算其周长

//创建全局变量
double pi = 3.14;
//class代表设计一个类,后面紧跟类的名称
class Circle
{
	//一个类需要具备的要素;访问权限,属性,行为
	//访问权限
public:
	//属性
	int m_r;
	//行为
	//获取圆的周长
	double calculator()
	{
		return 2 * pi * m_r;
	}

};
int main()
{
	//通过该类,创建具体的对象(实例化)
	Circle c1;
	//给圆对象进行属性的赋值
	c1.m_r = 10;
	//使用行为(函数)进行相应操作
	cout << "圆的周长为:  " << c1.calculator() << endl;
	system("pause");
	return 0;
}
#include<string>;

class Student
{
public:
	//字符串使用string,同时引用相应头文件
	string S_name;
	//id
	int S_studentcard;
	void S_showname()
	{
		cout << "学生的名字是" <<S_name << endl;
	}
	void S_showcard()
	{
		cout << "学生的学号是" << S_studentcard << endl;
	}
};
int main()
{
	Student ZZM;
	//字符串使用双引号,单字符使用单引号
	ZZM.S_name = "张泽明";
	ZZM.S_studentcard = 2023;
	ZZM.S_showcard();
	ZZM.S_showname();

	system("pause");
	return 0;
}

设计类时,可以把属性和行为放在不同的权限下,加以控制 

//三种访问权限:public(公共权限)protected(保护权限)private(私有权限)
//public  成员类内可以访问,类外也可以访问
//protected 类内可以访问 类外不可以访问 
//private   类内可以访问 类外不可以访问
//后两个在继承里面体现出区别看,子类可以访问父类中的保护内容,不可以访问私有内容
class person
{
public:
	string M_name;
private:
	string M_passport;
protected:
	string M_ID;
public:
	void func()
	{
		//类内可以访问
		M_ID = "123456";
		M_name = "zhangsan";
		M_passport = "123123";
	}
};
int main()
{
	//实例化对象
	person p1;
	p1.M_name = "nima";
	// 这类内容是访问不到的
	// p1.M_ID = "goode";
	//函数也一样
	p1.func();
	system("pause");
	return 0;
}

在C++中class和struct默认的访问权限不同
struct默认权限是 public
class默认权限是  private

默认权限就是,不进行权限设置,属性和行为的访问权限

权限设置技巧

class Person
{
public:
	//通过行为的设置进行信息的读取
	void setname(string name)
	{
		string M_name = name;
	}
	string getname()
	{
		return M_name;
	}

private:
	//将属性放置在私有权限内
	string M_name;
	string M_car;
	string M_id;
};

 函数传入一个实例的格式

bool func(cube& c)
{

}

构造函数和析构函数:

//构造函数和析构函数是为了完成对象的初始化和清理操作
//如果我们不提供,编译器会自动提供(空实现)
//对象的初始化和清理
class person
{
	//1.构造函数,对象的初始化操作
	//没有返回值 不用写void
	//函数名与类名相同
	//构造函数可以有参数,可以发生重载
	//创建对象时,构造函数会自动调用,且只调用一次
	person()
	{

	}
	//2.析构函数 进行清理操作
	//没有返回值,不写void
	//函数名与类名相同 在名称前加上~
	//析构函数不存在参数,不能发生重载
	//对象在销毁前 会自动调用析构函数,而且只会调用一次
	~person()
	{

	}
	//system("pause")就是在这一步停止的意思
};

构造函数的分类和调用

//构造函数:
//两种分类方式:
//按参数分为:有参构造和无参构造 
//按类型分为:普通构造和拷贝构造
//三种调用方式:
//括号法 显示法 隐式转换法

//分类
class person
{
public:
	//构造函数
	person()//无参
	{
	}
	person(int a)//有参
	{
	}
	//拷贝构造函数
	person(const person &p)
	{
	}
	//析构函数
	~person()
	{
	}
};
//构造函数的调用
int main()
{
	//1.括号法
	person p1;//无参
	person p2(10);//有参
	person p3(p2);//拷贝函数
	//注意事项,调用无参构造函数时,不要加()因为会被认为是一个函数声明
	//2.显示法
	person p4;
	person p5 = person(10);//有参
	person p6 = person(p5);//拷贝构造
	person(10);//匿名对象,执行此行之后,系统会立刻回收该对象
	//注意事项:不要利用拷贝构造函数初始化匿名对象,因为其相当于进行实例化
	//3.隐式转换法
	person p7 = 10;//相当于 person p7 = person(10);
	system("pause");
	return 0;
}

拷贝函数调用时机     

//通常有三种调用情况
class person
{
public:
	int M_age;
	person()
	{
		cout << "无参构造函数调用" << endl;
	}
	person(int a)
	{
		cout << "有参构造函数调用" << endl;
	}
	person(const person& p)
	{
		cout << "拷贝构造函数调用" << endl;
	}
	~person()
	{
		cout << "析构函数调用" << endl;
	}
};
//1.使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
	person p1 (20);
	person p2(p1);
	cout << p2.M_age << endl;
}
//2.值传递的方式给函数参数传值
void work(person p)
{

}
void test02()
{
	person p;
	//这里调用一个拷贝函数,创建新的数据传入work
	work(p);
}
//3值方式返回局部对象
person work2(person p)
{
	person p1;
	//调用拷贝函数,返回局部变量的值
	return p1;
}

构造函数的调用规则:

//默认情况下,编译器会给一个类添加三个函数
//1.默认构造函数(无参,函数体为空)
//2.默认析构函数(无参,函数体为空)
//3.默认拷贝构造函数,对属性进行值拷贝
//构造函数调用规则:
//如果用户定义有参构造函数,编译器不再提供默认无参构造函数,但是会提供默认拷贝函数(没有默认构造函数,调用报错)
//如果用户定义拷贝构造函数,编译器不会再提供其他构造函数(同理)

深拷贝与浅拷贝 

//浅拷贝:简单的拷贝赋值操作
//深拷贝:在堆区重新申请空间,进行拷贝操作
class person
{
public:
	person()
	{
		cout << "无参构造函数调用" << endl;
	}
	person(int age, int height)
	{
		m_age = age;
		int* m_height = new int(height);
		cout << "有参构造函数调用" << endl;
	}
	person(const person& p)
	{
		m_age = p.m_age;
		//m_height = p.m_height;编译器实现这行代码的时候,会导致堆区数据被重复释放,所以要重新申请重新使用
		int m_height = new int(*p.m_height);
	}
	~person()
	{
		//析构代码,将堆区开辟数据做释放操作
		if (m_height!=NULL)
		{
			delete m_height;
			m_height = NULL;
		}
	}
	int m_age;
	int* m_height;
};

 初始化列表

//c++的初始化列表
class person
{
public:
	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;
};

 类对象作为类成员 

//类对象作为类成员
//构造函数:先调用成员的构造函数
//析构函数:先调用自身的析构函数
class phone
{
public:
	phone(string pname)
	{
		p_name = pname;
	}
	string p_name;
};
class person
{
public:
	//这里类似于隐式转换 phone p = p_name;
	person(string age, string p_name) :m_age(age), p(p_name)
	{

	}
	int m_age;
	phone p;
};

 静态成员:

class person
{
public:
	//静态成员
	//所有成员使用一个数据
	//编译时就分配内存
	//类内声明,类外初始化
	static int m_A;
	//静态成员同样具有访问权限
private:
	static int m_b;
};
//类外初始化 person:: 代表person下的成员
int person::m_A = 10;
int person::m_b = 20;
void test01()
{
	//静态成员的两种访问方式
	//通过对象进行访问
	person p;
	cout << p.m_A << endl;
	//通过类进行访问
	cout << person::m_A << endl;
}

 静态函数

//静态函数
//所有对象共享一个函数
//静态函数只能访问静态成员变量
class person
{
	//静态函数同样存在权限访问问题
public:
	static void func()
	{
		m_a = 20;
		//m_b = 20;不能访问该变量
	}
	static int m_a;
	int m_b;
};
int person::m_a = 10;
void func()
{
	//两种访问方式
	person p;
	p.func();
	//类名访问
	person::func();
}

成员变量和成员函数分开存储:

class person
{
	//空对象大小是1
	//c++编译器会给每个空对象分配一个字节空间,为了区分空对象占内存的位置
	//非静态成员变量属于类上
	int m_a;
	//静态成员变量不属于类上
	static int m_b;
	//非静态成员函数不属于类上
	void func(){}
	//静态成员函数不属于类上
	static void func(){}
};
int person::m_b = 10;
void test01()
{
	person p;
	cout << sizeof(p) << endl;
}


int main()
{
	test01();
	system("pause");
	return 0;
}

this指针:

//this指针的作用(每个非静态成员函数都有,不用定义直接调用)
//1.解决名称冲突,当形参和变量名称一样时
class person
{
public:
	person(int age)
	{
		//this指向的是被调用成员函数所属的对象,就是创建的person p
		this->age = age;
	}
	//这里使用person的引用才能对创建的对象进行数据修改,不然就是简单的数据拷贝不能叠加
	person& addage(person p)
	{
		this->age += p.age;
		//返回对象本身使用*this
		return *this;
	}

	int age;
};
//2.返回对象本身用*this
void test01()
{
	person p1(10);
	person p2(10);
	//链式编程思想
	p1.addage(p2).addage(p2).addage(p2);

}

空指针访问成员函数:

class person
{
public:
	void func()
	{
		//一般这里会隐形地给出this->m_age,因此使用空指针指向这个函数就会出现问题
		//使用if判断一下,为空就直接return
		cout << m_age << endl;
	}
	int m_age=18;
};
void test01()
{
	person *p=NULL;
	p->func();
}
int main()
{
	test01();
	return 0;
}

常函数:

class person
{
	//const本质上是修饰的this指针常量,使得指针常量指向的值也不能修改
	void func()const
	{
		this->m_a = 10;
		this->m_b = 10;
	}
	int m_a;
	mutable int m_b;//被mutable修饰的量可以在常函数中进行修改
};

常对象:

void test01()
{
	//常对象不能修改成员属性
	//mutable修饰的可以修改
	const person p1;
	//常对象不能调用普通成员函数,能调用常函数
}

全局函数作友元:

//友元使对象可以访问private的属性数据
class Building
{
	//friend 放在函数前面
	friend void test01(Building* B);
public:
	string livingroom;
private:
	string bedroom;
public:
	Building()
	{
		livingroom = "kt";
		bedroom = "ws";
	}

};
void test01(Building *B)
{
	cout << B->livingroom << endl;
	cout << B->bedroom << endl;
}

 类作友元:

class Building
{
	friend class goodgay;
public:
	//在类外写函数,类内的函数就不能有函数体包括大括号{}
	Building() ;
	string livingroom;
private:
	string bedroom;
};
class goodgay
{
public:
	goodgay() ;
	void visit() ;
private:
	Building* building;
};
Building::Building()
{
	livingroom = "keting";
	bedroom = "woshi";
}
goodgay::goodgay()
{
	building = new Building;
}
//注意类外写函数的格式
void goodgay::visit()
{
	cout << building->bedroom<<endl;
	cout << building->livingroom<<endl;
}
void test01()
{
	goodgay g1;
	g1.visit();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 成员函数作友元:

friend goodgay::visit();

运算符重载的格式实际上就是函数的格式再稍微改变一下

加号运算符重载:

//运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
//运算符重载也能发生函数重载
//1.成员函数作运算符重载
class person
{
public:
	person(int a, int b)
	{
		m_A = a;
		m_B = b;
	}
	person operator+(person& p)
	{
		this->m_A += p.m_A;
		this->m_B += p.m_B;
		return *this;
	}
	int m_A ;
	int m_B ;
};
//2.全局函数作运算符重载
person operator+(person& p1, person& p2)
{
	person temp(0,0);
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
}
void test01()
{
	person p1(10, 10);
	person p2(10, 10);
	person p3(0, 0);
	p3 = p1 + p2;
	cout << p3.m_A << endl << p3.m_B<<endl;

}
int main()
{
	test01();
	system("pause");
	return 0;
}

移位运算符重载:

//cout是ostream类的一个对象
//不能使用成员函数进行移位运算符重载,因为产生不了相应的效果
class person
{
public:
	friend ostream& operator<<(ostream& cout, person& p);
	person(int a,int b)
	{
		m_a = a;
		m_b = b;
	}
private:
	int m_a;
	int m_b;
};
//返回值为ostream&,符合链式编程思想
ostream& operator<<(ostream& cout, person& p)
{
	cout << p.m_a << p.m_b;
	return cout;
}
void test01()
{
	person p1(10, 10);
	cout << p1 << endl;
}

递增运算符重载:

//成员函数实现递增运算符重载
class intage
{
	friend ostream& operator<<(ostream& cout, intage i);
public:
	intage(int a)
	{
		m_num = a;
	}
	//前置递增
	//必须使用intage&,不然就会出问题
	intage& operator++()
	{
		m_num++;
		return*this;
	}
	//占位参数实现后置递增,加上一个int代表后置递增
	//这里必须使用intage,因为返回的temp是局部变量,会被释放掉,不过还是会出问题
	intage operator++(int)
	{
		int temp = m_num;
		m_num++;
		return temp;
	}

private:
	int m_num;
};
//全局函数实现<<重载
ostream& operator<<(ostream&cout,intage i)
{
	cout << i.m_num;
	return cout;
}
void test01()
{
	intage in = 10;
	cout << ++(++in) << endl;
	cout << in << endl;
	cout << (in++)++<< endl;
	cout << in<< endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

赋值运算符重载:

//c++编译器会给一个类添加一共四个函数
//默认构造函数,默认析构函数,默认拷贝构造函数
//赋值运算符 operator=,对属性进行值拷贝
//可以解决浅拷贝导致的指针重复释放的问题

关系运算符重载(大于 小于 不等于 等于==)

跟前几个差不多

函数调用运算符重载,就是()

在类中重载的(),被称为仿函数,也跟上面的差不多

继承:

利用继承,可以减少重复代码

//继承语法:class 子类:继承方式 父类
class base
{
public:
	void b()
	{
		cout << 123 << endl;
	}
};
class child :public base
{

};

 继承方式:

子类的对象模型:

class base
{
public:
	int m_a;
	//private仍然被继承下来,但是被隐藏,没法调用
private:
	int m_b;
protected:
	int m_c;
 };
class son :public base
{
public:
	int m_d;
};
void test01()
{
	//结果是16,子类会继承父类中的所有数据
	cout << sizeof(son) << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

构造和析构的顺序:

先构造父类,再构造子类,析构相反

继承中,同名成员处理方式 

class base
{
public:
	void func() {};
	void func(int a) 
	{
		cout << a << endl;
	};
	int m_a;
};
class son :public base
{
public:
	void func() {};
	int m_a;
};
void test01()
{
	son s;
	//调用子类,直接调用
	s.m_a;
	s.func();
	//调用父类,就加作用域即可
	s.base::func(100);
	s.base::m_a;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

同名静态成员的处理方式:

//同名静态成员属性
class base
{
public:
	static int m_a;
};
int base::m_a = 100;
class son :public base
{
public:
	static int m_a;
};
int son::m_a = 50;
void test01()
{
	//1.通过作用域访问
	//2.通过类名进行访问
	cout << son::m_a << endl;
	cout << son::base::m_a << endl;
	//静态成员函数也一样
}

继承的语法:

//多继承
class son :public base1, public base2
{

};

菱形继承:

两个类同时继承自一个类,再同时继承给一个子类,那么该子类对于第一个类的数据继承了两份

class animal
{
public:
	int m_a;
};
//利用虚继承解决菱形继承问题
//虚继承,继承一个指针列表,指向该数据(大概)
class sleep:virtual public animal{};
class tuo:virtual public animal{};
class sleeptuo:public sleep,public tuo{};
void test01()
{
	sleeptuo st;
	//不会报错
	st.m_a;
}

 多态:

//静态多态:函数重载 运算符重载属于静态多态
//动态多态:派生类和虚函数实现运行时多态
//二者区别:
//静态多态:函数地址早绑定 编译阶段确定函数地址
//动态多态:函数地址晚绑定 运行阶段确定函数地址
//多态满足条件:1.有继承关系2.子类重写父类中的虚函数
//多态使用条件:父类指针或引用指向子类对象
class animal
{public:
	//添加virtual使地址晚绑定,传入参数之后再绑定,使speak处于多种状态
	virtual void speak()
	{
		cout << 1;
	}
};
class cut :public animal
{
public:
	void speak()
	{
		cout << 2;
	}
};
class dog :public animal
{
public:
	void speak()
	{
		cout << 3;
	}
};
void dospeak( animal& animal)
{
	animal.speak();
}
void test()
{
	cut c;
	dospeak(c);
	dog d;
	dospeak(d);
}
int main()
{
	test();
	system("pause");
	return 0;
}

 多态实现计算机类

#include<iostream>
using namespace std;
#include<string>
//利用多态实现计算机类
// 开闭原则:多扩展进行开放,对修改进行关闭
//多态的好处:
//1.组织更加清晰
//2.可读性强
//3.利于前期和后期的维护
//创建一个计算机的抽象类
class AbstrateCalcultor
{
public:
	virtual	int getResult()
	{
		return 0;
	}
	int Num1=0;
	int Num2=0;
};
//加法类
class AddCalcultor :public AbstrateCalcultor
{
	int getResult()
	{
		return Num1 + Num2;
	}
};
//减法类
class SubCalcultor :public AbstrateCalcultor
{
	int getResult()
	{
		return Num1 - Num2;
	}
};
//乘法类
class MutiCalcultor :public AbstrateCalcultor
{
	int getResult()
	{
		return Num1 * Num2;
	}
};
void test01()
{
	AbstrateCalcultor* abs = new AddCalcultor;
	abs->Num1 = 1;
	abs->Num2 = 2;
	cout << abs->Num1 << "+" << abs->Num2 << "=" << abs->getResult() << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 纯虚函数和抽象类

class base
{
public:
	//类中只要有一个纯虚函数,就被称为抽象类
	//纯虚函数
	virtual void func() = 0;
	//抽象类的特点:
	//1.无法实例化对象
	//2.子类必须重写父类中的纯虚函数,否则也无法实例化对象
};
class son :public base
{
public:
	virtual void func()
	{
		cout << "abs" << endl;
	}
};

 虚析构和纯虚析构

//虚析构和纯虚析构
//为了解决父类指针释放子类对象的问题
//子类出现开辟数据然后释放的操作时需要
//存在纯虚析构函数的类也称为抽象类
class Animal
{
public:
	virtual void speak() = 0;
	
	Animal()
	{
		cout << 1 << endl;
	}
	//虚析构
	virtual ~Animal()
	{
		cout << 2 << endl;
	}
	//纯虚析构,需要在外面实现一下
	virtual ~Animal() = 0;
};
Animal::~Animal()
{

}
class Cat :public Animal
{
public:
	virtual void speak()
	{
		cout << *Name << "在说话" << endl;
	}
	Cat(string C_Name)
	{
		Name = new string(C_Name);
		cout << 3 << endl;
	}
	~Cat()
	{
		cout << 4 << endl;
		if (Name != NULL)
		{
			delete Name;
			Name = NULL;
		}
	}
	string* Name;
};
void test01()
{
	Animal* an = new Cat("name");
	an->speak();
	//delete的是父类指针,无法进入子类虚构代码,因此使用虚析构
	delete an;
}
int main()
{
	test01();
	system("pause");
    return 0;
}
//析构函数的调用时机:创建实例向其中传值或者其他实例的时候
//析构函数的调用时机:销毁实例或者数据的时候

文件操作

//程序运行时产生的数据属于临时数据,运行结束就会被释放掉,通过文件可以将数据永久化
//使用文件要包含头文件文件流<fstream>
//文件类型:
//1,文本文件:以文本的ASCII形式存储
//2,二进制文件:文本以二进制形式存储,一般不能直接读取
//操作文件的三大类:
//1.ofstream 写操作
//2.ifstream 读操作
//3.fstream  读写操作

写文件:

//1.包含头文件
#include<fstream>
//2.创建流对象(写操作)
ofstream ofs;
void test()
{
	//3.打开文件
	ofs.open("666.txt", ios::out);
	//4.写文件
	ofs << "123" << endl;
	ofs << "456" << endl;
	ofs << "789" << endl;
	//5.关闭文件
	ofs.close();
}
int main()
{
	test();
	system("pause");
	return 0;
}

读文件:

#include<fstream>
//读文件
ifstream ifs;
void test()
{
	ifs.open("666.txt", ios::in);
	//判断是否读取成功
	if (!ifs.is_open())
	{
		cout << "读取失败" << endl;
	}
	//三种读取数据的方式
	// 一行一行地读的
	//
	char buf[1024] = { 0 };
	while (ifs >> buf)
	{
		cout << buf << endl;
		cout << 1;
	}
	//
	char buf[1024] = { 0 };
	while (ifs.getline(buf, sizeof(buf)))
	{
		cout << buf << endl;
		cout << 1;
	}
	//
	string buf;
	while (getline(ifs, buf))
	{
		cout << buf << endl;
	}
	//关闭文件
	ifs.close();
}
int main()
{
	test();
	system("pause");
	return 0;
}

 二进制文件:

 写文件

#include<fstream>
class Person
{
public:
	char Name[64];
	int Age;
};
//创建流对象和打开文件可以写在同一个地方
ofstream ofs("write.txt", ios::out | ios::binary);
void test()
{
	Person p = { "张三",18};
	//写文件
	ofs.write((const char*)&p,sizeof(&p));
	ofs.close();

}
int main()
{
	test();
	return 0;
}

读文件 

#include<fstream>
class Person
{
public:
	char Name[64];
	int Age;
};
ifstream ifs;
void test()
{
	Person p;
	ifs.open("write.txt", ios::in | ios::binary);
	//判断是否读取成功
	if (!ifs.is_open())
	{

	}
	ifs.read(( char*) & p, sizeof(Person));
	cout <<p.Name << endl << p.Age << endl;
}
int main()
{
	test();
	system("pause");
	return 0;
}

 

 

 

  


http://www.kler.cn/a/391562.html

相关文章:

  • 深入探讨 MySQL 配置与优化:从零到生产环境的最佳实践20241112
  • 《DiffusionDet: Diffusion Model for Object Detection》ICCV2023
  • BERT配置详解1:构建强大的自然语言处理模型
  • react 中 FC 模块作用
  • 初探鸿蒙:从概念到实践
  • uni-app中使用 unicloud 云开发平台③
  • TortoiseSVN提示服务器凭证检核错误:站点名称不符
  • windows下QT5.12.11使用MSVC编译器编译mysql驱动并使用详解
  • STM32学习笔记------GPIO介绍
  • SpringCloudAlibabaSidecar整合异构微服务
  • ES6模块、CommonJS、AMD等不同的模块化实现。
  • npm i 的时候报错: npm ERR! Error: EPERM: operation not permitted, rename
  • 已解决:spark代码中sqlContext.createDataframe空指针异常
  • 优化Mac的鼠标使用体验超简单方法
  • C++零基础趣味学信息学奥赛系列课程简介
  • 科技云报到:数字化转型,从不确定性到确定性的关键路径
  • Java的六大排序
  • react-router-dom 库作用
  • C++知识回顾
  • 游戏之地图找怪进行PK升级。C++
  • hive alter table add columns 是否使用 cascade 的方案
  • Linux后台运行jar包,nohup、>、
  • 源码解析-Spring Eureka
  • Qt 获取当前系统中连接的所有USB设备的信息 lsusb版
  • Spring Boot编程训练系统:架构设计与技术选型
  • creo toolkit二次开发学习之获取任意选择模型作为元件,并进行获取约束等