C++笔记

0.初识C++

0.1我的第一个C++程序

#include<iostream>
int main()
{
	using namespace std;
	cout << "Come up and C++ me some time" << endl;
	return 0;
}
  • 如果编译器不是最新需要使用<iostream.h>

0.2main()函数

0.2.1 main()

在C语言中,省略返回类型相当于说函数的类型为int。而C++逐渐淘汰这种用法

0.2.2 int main(void)

C++中,括号中的void表示函数不接受任何参数,与void main()等效。void main()同时省略返回语句。

0.3C++注释

C++注释以(//)打头,到行尾结束。注释可以位于单独的一行上,也可以和代码位于同一行。

0.4预处理和iostream

0.4.1预处理

程序在进行主编译之前需对源文件进行预处理:

#include<iostream>
using namespace std.
//OR
#include<iostream.h>
using namespace std.

0.4.2iostream

iostream中的io指的是输入和输出。为了使用cout来显示消息,需要这样定义。

使用cin和cout进行输入和输出的程序必须包含iostream文件

0.5头文件名

C++头文件,没有拓展名,对于C++来说去掉h不只是形式上的变化,没有h的头文件也可以包含名称空间。

0.6名称空间

using namespace std.

名称空间是为了使编写将多个厂商已有的代码组合起来的程序更简单而设计的。让厂商能够将其的产品封装在一个叫做名称空间的单元中。

类、函数和变量便是C++的标准组件,他们被放置在名称空间std中。iostream中定义的cout和endl实际上为std::coutstd::endl

0.6.1第一种方式

std::cout<<"come up and C++ me some time."<<std::endl;

0.6.2第二种方式

using namespace std;

0.6.3第三种方式

using std::cout;
using std::endl;

0.7输出

C++用cout进行输出。从概念上来看,输出的是一个流,即从程序流出的一系列字符。cout的对象表示这种,cout的对象属性包括一个插入操作符(<<),它可以将其右侧的信息插入到流中

cout<<string;//输出字符串
cout<<"come up and C++ me some time.";

操作符重载:插入操作符—>左移操作符

&:地址操作符—>AND操作符

​ ** :乘法—>对指针解除引用*

endl: 重启一行。==换行符: \n

0.8声明

被存储的数据类型 变量名;

int Liangzixihaoshuai;

0.9赋值

变量=值;

0.10输入

cin>>

C++将输出看作是流入程序的数据流。输入时,cin使用>>操作符从输入流中抽取字符。

<<和>>被选择用来指示信息流的方向

0.11cout拼接

cout<<liang<<zi<<xi<<haoshuai;

0.12类简介

cout是一个ostream 类对象。

cin 是一个istream类对象。

类是用户定义的类型,描述了一种数据类型的全部属性,而对象是根据这些描述创建的实体。

0.13函数

0.13.1函数调用

x=sqrt(6.25);//被调用函数sqrt()

以上需包含头文件cmath

0.13.2函数变体

接受多个参数:

double pow(double,double);

不接受任何参数:

int rand(void);

没有返回值:

void bucks(double);
bucks(12345.6);//调用语句

0.13.3定义函数

无返回值:

#include<iostream>
void maluyao(int);
int main()
{
	using namespace std;
	maluyao(3);
	cout << "pick an integer:";
	int count;
	cin >> count;
	maluyao(count);
	cout << "done!" << endl;
	return 0;
}
void maluyao(int n)//函数头
{
	using namespace std;
	cout << "liang zixi is so cool!" << n << "times" << endl;
}//函数体

C++不允许将函数定义嵌套

有返回值:

#include<iostream>
int maluyao(int);
int main()
{
	using namespace std;
	int stone;
	cout << "enter the weight in stone:\n";
	cin >> stone;
	int pounds = maluyao(stone);
	cout << stone << "stone=";
	cout << pounds << "pounds." << endl;
	return 0;
}
int maluyao(int n)//接受参数
{
	return 14 * n;
}//返回值

0.14using指令

using namespace std;

用于访问位于名称空间的std中的cout定义。

  • 将using std namespace;放在函数定义之前,让文件中所有的函数都能够使用名称空间std中所有的元素。
  • 将using std namespace;放在函数定义中,让该函数能够使用名称空间std中的所有元素。
  • 在特定的函数中使用类似using std::cout;让函数能偶使用指定的元素。
  • 在需要使用名称空间std中的元素时,使用前缀std::。

1.处理数据

1.1简单变量

1.1.1变量名

  • 名称中只能使用字母字符、数字和下划线
  • 第一个字符不能是数字
  • 区分大小写
  • 不能用C++关键字
  • 以两个下划线或下划线和大写字母打头的名称(_time_stop)被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符
  • C++对于名称的长度没有限制

1.2整型

1.2.1 short int long

  • short至少16位(2 bytes)
  • int至少与short一样长(4 bytes)
  • long至少32位(4 bytes)

1.2.2 sizeof limits

cout<<sizeof(int);
cout<<sizeof n_short;//括号可选

头文件climits定义了符号常量来表示类型的限制。

INT_MAX表示int能够存储的最大值。

INT_MIN表示int能够存储的最小值。

以此类推…

1.2.3初始化

int n_int =INT_MAX;
int wrens(432);

如果不对函数内部定义的变量进行初始化,该变量的值将是不确定的。这意味着该变量的值将是它被创建之前,该内存单元保存的值。

1.2.4无符号类型

优点:可以增大变量能够存储的最大值。

short: -32768到+32767

unsigned short:0到65365

signed char: -128到127

unsigned char:0到225

C++确保了超越了限制其值为范围另一端取值的问题,但不能保证符号整型超越限制(上溢或下溢)时不出错。

1.2.5整型常量

#include <iostream>
int main()
{
	using namespace std;
	int x = 42;
	int y = 042;
	int z = 0x42;

	cout << "十进制=" << x << endl;
	cout << "八进制=" << y << endl;
	cout << "十六进制=" << z << endl;
	return 0;
}
  • 第一位1~9,则为十进制。
  • 第一位是0,第二位为1~7,则为八进制。
  • 前两位为0x,则为十六进制。

1.2.6char类型

char类型专为存储字符而设计的。字符集中的字符用数值编码(ASCII码)表示。

#include<iostream>
int main()
{
    using namespace std;
    char ch='M';
    cout.put(ch);//使用cout.put()函数来显示字符常量
    cout<<endl;
}

cout.put():句点被称为成员操作符。通过类对象cout来使用函数put()。可以替代<<操作符。

1.2.7wcha_t

wchar_t(宽字符类型)可以表示拓展字符集。是一种整数类型,它有足够的空间可以表示系统使用的最大拓展字符集。

1.2.8 bool类型

布尔变量值可以是true或flase。字面值的true和false可以转换为int的1和0。

bool isready=true;
int ans=true;//ans被赋值为1
int promise=false;//promise被赋值为0
bool start=-100;//start被赋值为true
bool stop=0;//任何数值或指针值都可以被隐式转换为bool值,非零为true,零为false

1.2.9 const限定符

对比C遗留的#define,C++中有一种更好的处理符号常量的办法:

利用const关键字来修改变量声明和初始化:

const type name=value;

1.3 浮点数

1.3.1表示法

  • 标准小数点表示法: 12.34
  • E表示法: 3.45E6,2.52e+8

d.dddE+n指的是将小数点向右移n位,而d.dddE-n指的是将小数点向左移n位。

1.3.2浮点常量

8.24 double型

2.45E20F float型(加F/f后缀)

1.3.3优点

  • 表示整数之间的值

  • 表示范围大

1.4 算术操作符

1.4.1 基本算术操作符

+:加法运算。

-:减法运算。

*:操作数相乘。

/:两个操作数都是整数,则结果为商的整数部分,小数部分被丢弃。

如果其中至少有一个操作数是浮点数,则小数部分将保留,结果为浮点数。

%:求模操作符,生成第一个数除以第二个数的余数。

1.4.2 类型转换

1.4.2.1条件
  • 将一种算术类型的值赋给另一种算术类型的变量时。
  • 表达式中包含不同的类型时。
  • 将参数传递给函数时。
1.4.2.2赋值时类型转换

将一个值赋给取值范围更大的值不会造成什么问题。

但将很大的值赋给小的值会降低精度。

将0赋给bool变量时,将被转换为false,非零值会被转换为true。

1.4.2.3表达式中的转换

long double<-double<-float<-unsigned long<-long int/unsigned int(取决于两种类型的相对长度)<-long<-unsigned int<-int

1.4.2.4强制类型转换

强制类型转换不会修改变量本身,而是创建一个新的、指定类型的值,可以在表达式中使用这个值。

(typeName) value//来自c
 typeName (value)//纯粹C++

2 复合类型

2.1 数组

2.1.1数组声明

typeName arrayName[arraySize];
short months[12];

表达式arraySize指定元素数目,必须是整数或const值,也可以是常量表达式。不能是变量。

#include <iostream>
int main()
{
	using namespace std;
	int array[3];
	array[0] = 7;
	array[1] = 8;
	array[2] = 6;
	static int Array[3] = { 20,30,5 };//对函数定义的常规数组进行初始化
	cout << "Total arrays =";
	cout << array[0] + array[1] + array[2] << endl;
	int total = array[0] * Array[0] + array[1] * Array[1];
	total = total + array[2] * Array[2];
	cout << "total=" << total << endl;
	cout << "size of array =" << sizeof array; << endl;
	cout << "size of one element=" << sizeof array[0];
	return 0;
}

2.1.2初始化数组

在定义数组时初始化

int card[4]={1,2,3,4};
int hand[4];
float totals[5]={5.0,2.5};//其他元素被设置为0
long total[500]={0};
short things[]={3,5,3,8};

2.2字符串

2.2.1 字符串输入

通过char字符输入字符串

char cat[5]={"f","a","s","t","\0"};

利用字符串常量/字符串字面值

char bird[10]="Mr.cheeps";
char fish[]="Bubbles";

2.2.2拼接字符串常量

以下等效:

cout<<"I'd give my arm to be""a great violinist.";
cout<<"I'd give my arm to be a great violinist.";

拼接时不会在连解的字符串之间添加空格,第一个字符串中的\0字符会被第二个字符串的第一个字符取代。

2.2.3数组中使用字符串

#include <iostream>
#include<cstring>
int main()
{
	using namespace std;
	const int Size = 15;
	char name1[Size];//将键盘或文件输入读入到数组中
	char name2[Size]="C++owboy";//将数组初始化为字符串常量
	cout << "what is your name"<<endl;
	cin >> name1;
	cout << strlen(name1)<<endl;//利用标准库函数来确定字符串的长度
	cout << "in an array of" << sizeof(name1)<<endl;
	return 0;
}

2.2.4 读取一行字符的输入

2.2.4.1 getline()

getline()函数读取整行,使用通过回车键输入的换行符来确定结尾。

cin.getline(存储输入行的数组的名称,读取的字符数)

cin.getline(name,20);
cin.getline(name,ArSize);
2.2.4.2 get()

与getline()类似,但get()不会丢弃换行符,而是将其留在输入队列中。

用get()读取两行

cin.get(name,Arsize);
cin.get();//读取新的一行
cin.get(dessert.Arsize);

将两个类成员函数拼接:

cin.get(name,Arsize).get();

2.2.5混合输入字符串和数字

int year;
cin>>year;
char address[80];
cin.getline(address,80);

在输入年份后。cin.getline()看到换行符后会认为是一个空行,将空字符串赋给address数组。

int year;
cin>>year;
cin.get();//or cin.get(ch);
char address[80];
cin.getline(address,80);

(cin>>year).get();//or (cin>>get).get(ch);

2.3 String 类

2.3.1 使用string对象

#include <iostream>
#include<string>
int main()
{
	using namespace std;
	string str1;
	string str2 = "panther";
	cin >> str1;
	cout << str1<<endl;
	cout << "The third letter is " << str2 << "is" << str2[2] <<endl;
}
  • 可以用c风格字符串来初始化
  • 可以通过cin来将键盘输入存储到string对象中
  • 用cout来显示string对象
  • 用数组表示法来访问存储在string对象中的字符

2.3.2 赋值、拼接、附加

可以将string对象赋给另一个string对象:

string str1;
string str2-"panther";
str1=str2;

string类字符串合并操作,使用操作符+将两个string对象相加,或将他们附加到string对象的末尾:

string s1-"penguin";
string s2,s3;
s2="Buzzed";
cout<<"s2="<<s2<<endl;
s3=s1+s2;
cout<<"s3="<<s3<<endl;
s2+="for a dey";
cout<<"a2+=\"for day\"yield s2="<<s2<<endl;

2.3.3 其他操作

strcpy(charr1,charr2);//将字符串复制到字符数组中
strcat(charr1,charr2);//将字符串附加到字符数组的末尾

2.4 结构

2.4.1结构使用

C++允许在声明变量时省略关键字struct

#include <iostream>
#include<string>
struct inflatable
{
	char name[20];
	float volume;
	double price;
};
int main()
{
	using namespace std;
	inflatable quest =
	{
		"Glorious Gloria",
		1.88 ,
		29.99
	};
	inflatable pal =
	{
		"Audacious Arthur",
		3.12,
		32.99
	};
	cout<< "Expand your quest list with" << quest.name;
	cout << "and" << pal.name << "!\n";
	cout << "You can have both for";
	cout << quest.price + pal.price << "!\n";
	return 0;
}

初始化方式:使用由逗号分隔的值列表,并将这些值用花括号阔起。

2.4.2其他属性

使用赋值操作符(=)将结构赋给另一同类型的结构。

inflatable bouquet=
{
    "sunflowers",
    0.20,
    12.49
};
inflatable choice;
choice=bouquet;

同时完成定义结构和创建结构变量:

struct perks
{
    int key_number;
    char car[12];
}mr_smith,ms_jones;

创建变量:

struct perks
{
    int key_number;
    char car[12];
}mr_smith=
{
    7,
    "packard"
};

2.4.3 结构数组

创捷一个100个inflatable结构的数组

inflatable array[100];
cin>>array[10].volume;

array本身是一个数组,因此向array.price这样的表述是无效的。

初始化结构体数组:初始化数组规则+初始化结构的规则

inflatable guest[2]=
{
    {"Bambi",0.5,21,99}
    {"Godzilla",2000,565,99}
};

2.5指针和自由存储空间

2.5.1地址和取地址

&:地址操作符。获得变量的位置。

*:间接值或解除引用操作符。可以得到该地址处存储的值。

2.5.2声明和初始化

指针声明必须指定指针指向的数据的类型

int *p_updates;//p_updates是指针(地址),而*p_updates是int,而不是指针
int *p1,p2;//创建一个指针(p1)和一个常规int变量(p2)

2.5.3指针的危险

在对指针应用解除引用操作符(*)之前,将指针初始化为一个确定的适当的地址。

2.5.4指针和数字

不能简单的将整数赋给指针。应当通过强制类型转换将数字转换为适当的地址类型:

int *pt;
pt=(int*)0x88000000;

pt是int值的地址,但不意味着是int类型,int是2个字节而地址则是4个字节。

2.5.5 new动态分配内存

在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值。

typeName pointer_name=new typeName;//需要在两个地方指定数据类型用来指定需要什么样的内存和用来声明合适的指针
#include<iostream>
int main()
{
	using namespace std;
	int* pt = new int;
	*pt = 1001;
	cout << "int";
	cout << "value=" << *pt << ":location=" << pt << endl;
	double* pd = new double;
	*pd = 10000001.0;
	cout << "double";
	cout << "value=" << *pd << "location=" << pd << endl;
	cout << "size of pt=" << sizeof(pt) << endl;
	cout << "size of *pt=" << sizeof(*pt) << endl;
	cout << "size of pd=" << sizeof(pd) << endl;
	cout << "size of *pd=" << sizeof(*pd) << endl;
	return 0;
}

2.5.6使用delete来释放内存

使用delete时,后面要加上指向内存块的指针:

int *ps=new int;
delete ps;

2.5.7 使用new创建动态数组

必须在类型名后面加上方括号,其中包含元素数目:

type_name pointer_name=new type_name [num_elements];
int *psome=new int[10];
delete []psome;//指出所要释放的是一个数组

2.5.8 指针和字符串

在cout和C++表达式中,char数组名、指向char的指针以及用引号括起的字符串常量都被解释为字符串第一个字符的地址。

在将字符串读入程序时,应使用已分配的内存的地址。该地址可以是数组名,也可以是使用new初始化过的指针。

2.5.9 使用new创建动态结构

创建一个未命名的inflatable类型,并将其地址赋给一个指针:

inflatable*ps=new inflatale;

利用箭头成员操作符(->)用于指向结构的指针。

如果结构标识符是结构名,则使用句点标识符。

如果标识符是指向结构的指针,则使用箭头操作符。

#include<iostream>
struct inflatable
{
	char name[20];
	float volume;
	double price;
};
int main()
{
	using namespace std;
	inflatable* ps = new inflatable;
	cout << "Enter name of inflatable item:";
	cin.get(ps->name, 20);
	cout << "Enter volume in cubic feet:";
	cin >> (*ps).volume;
	cout << "Enter price:";
	cin >> ps->price;
	cout << "Name:" << (*ps).name << endl;
	cout << "volume:" << (*ps).volume << endl;
	cout << "price:" << (*ps).price << endl;
	delete ps;
	return 0;
}
#include<iostream>
#include<string>
#include<ctime>
using namespace std;
struct Student 
{
	string sName;
	int score;
};
struct Teacher
{
	string tName;
	struct Student Array [5];
};
void allocateSpace(struct Teacher tArray[],int len)
{
	int i;
	string nameSeed = "ABCDE";
	for (i = 0; i < len; i++)
	{
		tArray[i].tName = "Teacher_";
		tArray[i].tName += nameSeed[i];
	}
	for (int j = 0; j < 5; j++)
	{
		tArray[i].Array[j].sName = "Student_";
		tArray[i].Array[j].sName += nameSeed[j];
		int random = rand() % 61 + 40;
		tArray[i].Array[j].score = random;
	}
}
void printfInfo(struct Teacher tArray[], int len)
{
	int i;
	for (i = 0; i < len; i++)
	{
		cout << "老师姓名" << tArray[i].tName << endl;
		for (int j = 0; j < 5; j++)
		{
			cout << "\t学生姓名" << tArray[i].Array[j].sName <<
				"考试分数" << tArray[i].Array[j].score << endl;
		}
	}
}
int main()
{
	srand((unsigned int)time(NULL));
	struct Teacher tArray[3];
	int len = sizeof(tArray) / sizeof(tArray[0]);
	allocateSpace(tArray, len);
	printfInfo(tArray,len);
	system("pause");
	return 0;
}
#include<iostream>
#include<string>
using namespace std;
struct Hero
{
	string name;
	int age;
	string sex;
};
void bubbleSort(struct Hero heroArray[], int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			if (heroArray[j].age > heroArray[j + 1].age)
			{
				struct Hero temp = heroArray[j];
				heroArray[j] = heroArray[j + 1];
				heroArray[j + 1] = temp;
			}
		}
	}
}
void printHero(struct Hero heroArray[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << "name=" << heroArray[i].name << "age=" << heroArray[i].age << "sex=" << heroArray[i].sex << endl;
	}
}
int main()
{
	struct Hero heroArray[5] =
	{
		{"刘备",23,"男"},
		{"关羽",22,"男"},
		{"张飞",20,"男"},
		{"赵云",21,"男"},
		{"貂蝉",19,"女"},
	};
	int len = sizeof(heroArray) / sizeof(heroArray[0]);
	bubbleSort(heroArray, len);
	printHero(heroArray, len);
	system("pause");
	return 0;
}
#include<iostream>
#include<string>
#define MAX 1000
using namespace std;
struct Person
{
	string m_Name;
	int m_Sex;
	int m_age;
	string m_Phone;
	string m_Addr;
};
struct Addressbooks
{
	struct Person personarray[MAX];
	int m_Size;
};
void showMenu()
{
	cout << "*****1.添加联系人*****" << endl;
	cout << "*****2.显示联系人*****" << endl;
	cout << "*****3.删除联系人*****" << endl;
	cout << "*****4.查找联系人*****" << endl;
	cout << "*****5.修改联系人*****" << endl;
	cout << "*****6.清空联系人*****" << endl;
	cout << "*****0.退出通讯录*****" << endl;
}
void addPerson(Addressbooks* abs)
{
	if (abs->m_Size == MAX)
	{
		cout << "通讯录已满" << endl;
		return;
	}
	else
	{
		string name;
		cout << "请输入姓名" << endl;
		cin >> name;
		abs->personarray[abs->m_Size].m_Name = name;
		cout << "请输入性别" << endl;
		cout << "1.---男" << endl;
		cout << "2.----女" << endl;
		int sex = 0;
		while (true)
		{
			if (sex == 1 || sex == 2)
			{
				abs->personarray[abs->m_Size].m_Sex = sex;
				break;
			}
			cout << "输入有误,请重新输入!" << endl;
		}
		cout << "请输入年龄:" << endl;
		int age = 0;
		cin >> age;
		abs->personarray[abs->m_Size].m_age = age;
		cout << "请输入联系电话" << endl;
		string phone;
		cin >> phone;
		abs->personarray[abs->m_Size].m_Phone = phone;
		cout << "请输入家庭住址" << endl;
		string address;
		cin >> address;
		abs->personarray[abs->m_Size].m_Addr = address;
		abs->m_Size++;
		cout << "添加成功" << endl;
		system("pause");
		system("cls");

	}
}
int main()
{
	Addressbooks abs;
	abs.m_Size = 0;
	int select = 0;
	while (true)
	{
		showMenu();
		cin >> select;
		switch (select)
		{
		case 1:
			addPerson(&abs);//利用地址传递
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			break;
		case 6:
			break;
		case 0:
			cout << "欢迎下次使用" << endl;
			system("pause");
			return 0;
			break;
		default:
			break;
		}
	}
	system("pause");
	return 0;
}

3.0内存分区模型

3.1代码区

存放函数体的二进制代码,有操作系统进行管理的。

未执行该程序前:

存放CPU执行的机器指令。

特点:共享,只读。

3.2全局区

存放全局变量和静态变量(static)以及常量(const修饰的全局常量和字符常量)。

未执行该程序前:

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

3.3栈区

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

不要返回局部变量的地址。

void func()
{
    int a=10;
    return &a; 
}
int main()
{
    int *p=func();
    cout<<*p<<endl;
    cout<<*p<<endl;//乱码
}

3.4堆区

又程序员分配和释放。

在C++中主要利用new在堆区开辟内存。

int*func()
{
    int*p=new int(10);//指针本质也是局部变量放在栈上,指针保存的数据放在堆上。
    return p;
}
int mian()
{
    int *p=func();
    return 0;
}

3.5new操作符

手动释放,释放利用操作符delete

int *func()
{
    int *p=new int(10);//返回的是该数据类型的指针
    return p;
}
void test01()
{
    int*p=func();
    cout<<*p<<endl;
    delete p;//释放内存
}

在堆区开辟数组

void test02()
{
    int *array=new int[10];//代表数组由10个元素
    for(int i=0;i<10;i++)
    {
        array[i]=i+100;
    }
    delete []array;//释放数组时要加[]
}

4.0引用

作用:给变量起别名

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

int &b=a;
b=20;
cout<<a<<endl;//a=20

4.1引用注意事项

  • 必须要初始化

    int a=10;
    int &b=a;
    int &b;//错误
    
  • 初始化后不可改变

int c=20;
b=c;//赋值操作

4.2引用做函数参数

可以利用引用计数让形参修饰实参

可以简化指针

void Swap(int &a,int &b)//a b分别是实参中的别名,可以通过修改别名来修改实参
{
    int temp=a;
    a=b;
    a=temp;
}
int main()
{
    int a=10;
    int b=20;
	swap(a,b);
}

4.3引用作为函数返回值

不要返回局部变量的引用

函数的调用可以作为左值

int& test01()
{
    int a=10;//存放在栈区
    return a;//返回局部变量
}
int& test02()
{
    static int a=10;//静态变量
    return a;
}
int main()
{
    int &ref=test01();
    cout<<ref<<endl;//第一次结果正确,编译器做了保留
    int &ref2=test02();
    cout<<ref2<<endl;
    test02()=1000;//将别名赋值1000,原名也改变
    cout<<ref2<<endl;//函数返回值作为左值
}

4.4引用的本质

在c++内部实现是一个指针常量。

int&ref=a;
int *const ref=&a;//指向不可改

编译器帮我们转化为*ref

4.5常量引用

主要用来修饰形参,防止误操作

int a=10;
const int &ref=10;//加上const之后 编译器将代码修改 int temp=10;const int &ref=temp;
ref=20;//加入const之后不可修改,只读
void showValue(const int &val)
{
    val=1000;//加入const后函数体内不能修改
    cout<<val;
}

5函数提高

5.1函数的默认参数

可以由默认值

返回值类型 函数名(参数=默认值);

int func(int a,int b=20,int c=30)
{
    return a+b+c;
}
int main()
{
    func(10);
}

传了就有传的值,没传就用默认值

如果某个位置已经有了默认参数,那么从这个位置往后,都必须拥有参数值(b有了,c也必须有)

如果函数声明有默认参数,则函数实现不能有默认参数

5.2函数占位参数

可以有占位参数,用来做占位,调用函数时必须填补改位置。

返回值 函数名(数据类型)

void func(int a,int)
{
    cout<<"this is func"<<endl;
}
int main()
{
    fun(10,10);//传一个相应的数据类型
}
void func(int a,int =10)//可以有默认参数
{
    .....
}

5.3函数重载

函数名可以相同,提高复用性

  • 同一个作用域下

  • 函数名称相同

  • 函数参数类型不同 或 个数不同 或 顺序不同

    函数的返回值不能作为函数重载的条件

    参数不同:

    void func()
    {
        cout<<"func的调用"<<endl;
    }
    void func(int a)//func(10)
    {
        cont<<"func的inta调用"<<endl;
    }
    

    类型不同:

    void func(int a)
    void func(double b)
    

    顺序不同:

    void func(int a,double b)
    void func(int b,double a)
    

    返回值不可以作为重载条件

    void func()
    int func()//二义性
    

    5.4重载注意事项

    引用作为重载条件

    void func()//int a=10;func(a)变量可读可写
    {
        cout<<"func()的调用"<<endl;
    }
    void func(const int &a)//func(10)const int&a=10;
    {
        cout<<"const func()的调用"<<endl;
    }
    

    函数重载碰到默认参数

    void func2(int a,int b=10)
    void func(int a)
    int main()
    {
        func2(10);/二义性,尽量避免
    }
    

6类和对象

封装、继承、多态

6.1封装

将属性和行为作为一个整体

将属性和行为加以权限控制

封装的意义1:属性和行为写在一起:

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

#include <iostream>
using namespace std;
const double PI 3.14
class Circle//设计类
{
    public://访问权限
    int m_r;//属性
    double calculateZC()
    {
        return 2*PI*m_r;//行为
    }
    
}
int main()
{
    Circle c1;//通过类创建一个对象的过程,实例化
    c1.m_r=10;
    cout<<calculateZC()<<endl;
    return 0;
}
#include<iostream>
#include<string>
using namespace std;
class Student
{
    public:
    string m_Name;
    int m_Id;
    void showStudent()
    {
        cout<<m_Name<<m_Id<<endl;
    }
}
int main()
{
    Student s1;
    s1.m_name="张三";
    s1.m_Id=2;
    s1.showStudent();
}
void setName(string name)
{
    m_Name=name;
}
s1.setName("张三");//张三->name->m_Name

类中的属性和行为统一称为成员

属性 成员属性,成员变量

行为 成员函数,成员方法

封装意义2:

  • 公共权限 public 成员类内可以访问,类外可以访问
  • 保护权限 protected 类内可以访问 类外不可以访问
  • 四有权限 private 类内可以访问 类外不可以访问
#include<iostream>
using namespace std;
class Person
{
public:
    string m_Name;
protected:
    string m_Car;
private:
    int m_Password;
public:
    {
        m_Name="张三";
        m_Car="拖拉机";
        m_Password=123456;
    }//类内可以访问 
    
}
int main()
{
    Person p1;
    p1.m_Name="李四";
    p1.m_Car="奔驰";
    p1.m_Password=123;//类外不可以访问
    system("pause");
}

6.2struct和class

struct默认权限为公共public

class默认权限为私有private

6.3成员属性设置为私有

优点1:自己有控制读写权限

优点2:可以检测数据有效性

#include<iostream>
#include<string>
class person
{
public://提供接口
void setName(string name)
{
    m_Name=name;
}
string getName()
{
    return m_Name;
}
int getAge()
{
    m_Age=0;//初始化为0岁
    return m_Age;
}
void setLover(string lover)
{
    m_Lover=lover;//内部可写
}
private:
    string m_Name;//可读可写
    int m_Age;//只读
    string m_Lover;//只写
}
int main()
{
    Person P;
    p.setName("张三");
    cout<<p.getName<<endl;
    cout<<p.getAge<<endl;
    p.setLover("苍井");//设置
    cout<<p.m_Lover<<endl;//外部不可访问
}
void setAge(int age)
{
    if(age=0||age>150)
    {
        cout<<"错误"<<endl;
        return;
    }
    m_Age=age;
}

6.4案例

#include<iostream>
using namespace std;
class Cube
{
public:
	void setL(int l)
	{
		m_L = l;
	}
	int getL()
	{
		return m_L;
	}
	void setW(int w)
	{
		m_W = w;
	}
	int getW()
	{
		return m_W;
	}
	void setH(int h)
	{
		m_H = h;
	}
	int getH()
	{
		return m_H;
	}
	int calculateS()
	{
		return 2 * m_L * m_H + 2 * m_L * m_W + 2 * m_W * m_H;
	}
	int calcualteV()
	{
		return m_L * m_W * m_H;
	}
	bool isSameByClass(Cube &c)
	{
		if (m_L == c.getL() && m_W == c.getW() && m_H == c.getH())
		{
			return true;
		}
		return false;

	}
private:
	int m_L;
	int m_W;
	int m_H;
};
//利用全局函数
bool isSame(Cube &c1,Cube &C2)//通过引用方式传递,不拷贝
{
	if (c1.getL() == C2.getL() && c1.getW() == C2.getW() && c1.getH() == C2.getH())
	{
		return true;
	}
	return false;
}
int main()
{
	Cube c1;
	c1.setL(10);
	c1.setH(10);
	c1.setW(10);
	cout << c1.calcualteV() << endl;
	cout << c1.calculateS() << endl;
	Cube c2;
	c2.setL(10);
	c2.setH(10);
	c2.setW(10);
	bool ret = isSame(c1, c2);
	if (ret)
	{
		cout << "相等" << endl;
	}
	else
	{
		cout << "不相等"<<endl;
	}
	ret = c1.isSameByClass(c2);
	if (ret)
	{
		cout << "相等" << endl;
	}
	else
	{
		cout << "不相等" << endl;
	}
	system("pause");
	return 0;
}

6.5对象的初始化和清理

6.5.1构造函数和析构函数

构造函数:主要利用于创建对象的成员属性赋值,构造哈桑农户有编译器自动调用,无须手动调用

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

class Person
{
public:
    Person()
    {
        
    }
    ~Person()
    {
        
    }
}
void test01()
{
    Person P;//系统自动调用构造析构函数
}

6.5.2构造函数的分类

有参数/无参数

普通/拷贝

class Person
{
    Person()//无参,默认
    {
        cout<<"person调用"<<endl;
    }
    Person(int a)//有参
    {
        cout<<”“<<endl;
    }
    Person(const Person&P)//拷贝,引用方式传入
    {
        age=p.age;//将传入的属性拷贝到我身上
        cout<<""<<
    }
}
void test01()
{
    //括号法
    Person P;//无参,不要加()
    Person P2(10);//有参
    Person P3(P2);//拷贝
    //显示法
    Person P1;
    Person P2=Person(10);
    Person P3=Person(P2);
    Person(10);//匿名对象 当执行借书后系统会立刻回收
    //不要利用拷贝构造函数来初始化匿名对象Person(P3)
    //隐式转换法
    Person P4=10;//Person P4=Person(10)有参
    Person P5=p4;//拷贝
}

6.5.3拷贝构造函数调用时机

  • 使用一个已经创建完毕的对象来初始化一个新的对象
  • 值传递的方式给函数参数传值
  • 以值的方式返回局部对象
class Person
{
public:
    Person()
    {
        cout<<"默认构造"<<erndl
    }
    Person(int age)
    {
        cout<<"有参构造"<<endl;
        m_Age=age;
    }
    Person(const person&P)
    {
        cout<<"拷贝构造"<<endl;
        m_Age=p.m_Age;
    }
    ~Person()
    {
        cout<<
    }
    
}
void doWork(Person P)
{
    
}
void doWork2()
{
    Person P1;
    return P1;//拷贝新的对象返回
}
void teat01()
{
    Person P1(20);
    Person P2(P1);//用P1初始化P2
}
void test02()
{
    Person P;
    doWork(P);//利用值传递给函数传值形参改变不会影响被拷贝数据
}
void test03()
{
 	 Person P=doWork2();   
}

6.5.4构造函数调用规则

  • 默认构造函数
  • 默认析构函数
  • 默认拷贝构造函数

规则:如果定义有有参构造,则不再提供无参构造,但会默认提供拷贝构造

如果它定义拷贝构造,则不会提供其他函数

6.5.5深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请内存空间,进行拷贝操作

class Person
{
public
	Person()
	{
		cout<<"默认"<<endl;
	}
	Person(int age)
	{
		m_Age=age;
		cout<<"有参"<<endl;
	}
	~Person(int age)
	{
	//将堆区开辟的数据做释放
	if(m_Height!=NULL)
	{
		delete	 m_Height;
		m_Height=NULL;//崩了!!!堆区内存重复释放
	}
		m_Age=age;
		cout<<"析构"<<endl;
	}
	Person(int age,int height)
	{
	m_Age=age;
	m_Height=new int(height);
	cout<<"有参",<<endl;
	}
}
void test01()
{
	Person p1(18);
	Person p2(p1);//浅拷贝,值传递
}
void test02()
{
	Person(18,160);
	Person p2(p1);//p1==p2
}

浅拷贝带来的问题:堆区内存重复释放。

深拷贝:重新创建一个内存,存放数据一样,指针指向内容不一样。

//自己实现拷贝构造函数
Person(const Person&p)
{
    cout<<"拷贝构造"<<endl;
    m_Age=p.m_Age;
    //m.Height=p.m_Height;编辑器默认实现的代码:浅拷贝
    //深拷贝操作:
    m_Height=new int(*p.m_Height);
}

6.6初始化列表

构造函数():属性1(值1),属性2(值2)…{}

class Person
{
public:
    //传统初始化
    Person(int a,int b,int c)
    {
    	m_A=a;
    	m_B=b;
    	m_C=c;
    }//Person P(10,20,30);
    //初始化属性
    Person():m_A(10)/*属性(赋初值)*/,m_B(20),m_C(30)
    {
        
    }//Person p;
    Person(int a,int b,int c):m_A(a),m_B(b),m_C(c);
    {
        
    }//Person P(30,20,10);
    int m_A;
    int m_B;
    int m_C;
}

6.7类对象作为类成员

允许类中的成员是另一个类的对象,称为 对象成员

class Phone
{
public:
    Phone(string pName)
    {
        m_PName=pName;
    }
    string m_PName;
}
class Person
{
public:
    //Phone m_Phone=pName 隐式转换法
    Person(string name,string pName):m_Name(name),m_Phone(p_Name)
    {
     cout<<   
    }
    string m_Name;
    Phone m_Phone;
}

当其他类的对象作为本类的成员:

先构造其他类的对象,再构造本类的对象。

先析构本类的对象,再析构其他类的对象。

6.8静态成员

静态成员变量

  • 所有对象共享一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量
class Person
{
public:
    static void func();//静态成员函数
    {
        m_A=100;//静态成员函数可以访问静态成员变量,共享的的一份m_A
        m_B=200;//非静态成员,函数无法区分到底是哪个对象的m_B
    };
    static int m_A;//静态成员变量
    int m_B
        //静态成员函数也是有访问权限的
private:
   static void func2()
   {
       
   }
};
void teat01()
{
    Person P;
    p.func();//通过对象进行访问
    Person::func();//通过类名访问
    Person::func2();//类外访问不到私有的静态成员函数
}
int Person::m_A=0;//静态变量类外初始化

6.9C++对象模型和this指针

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

只有非静态成员才属于类的对象上

空对象占用内存空间为:sizeof=1;

c++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占的内存的位置,每个空对象也应该有一个独一无二的内存地址

非静态成员变量占用内存为:sizeof=4;

不是空的,按照数据类型分配字节空间,属于类的对象上。按成员变量给类分配空间。

静态成员变量:sizeof=4;

不属于类的对象上

非静态成员函数:不属于类的对象上

静态成员函数:不属于类的对象上

6.9.2this指针

this指针指向被调用的成员函数所属的对象。

谁调用this,this就指向谁。

  • this指针是隐含每一个非静态成员函数内的一种指针
  • this指针不需要定义,直接使用

this指针用途

  • 当形参和成员变量同名时,可以用this区分
  • 在类的非静态函数中返回对象本身,可以使用return *this
class Person
{
public:
    Person(int age)
    {
        age=age;//属性age未被赋值,同名
    }
    void PersonAddAge(Person &p)
	{
	    this->age+=p.age;
	}

    int age;//成员常用m_Age
	}
void test01()//调用后不是18
{
    Person p1(18);
    cout<<
}

Person(int age)
{
    this->age/*p1所属对象*/=age;//this指针指向被调用成员函数所属对象
}
int age;
void test()
{
    Person P1(10);
    Person P2(10);
    p2.PersonAddAge(p1);
    cout<<p2.age<<endl;
}

使p2调完函数后返回p2本身,就可以一直执行这样的操作。

return* this;(this 是指向p2的指针,那么* *this就是指向p2的本体)

Person& PersonAddAge()//用引用的方式进行返回,用值的方式返回会产生一个新的对象

则可以使用p2.PersonAddAge(p1).PersonAddage//链式编程思想

6.10空指针访问成员函数

空指针也可以调用成员函数,但是有注意有没有用到this指针。

如果用到this指针,需要加以判断保证代码的健壮性

属性前默认有this指针,当传入的指针为Null会崩

if(this==NULL)
{
    return;
}//如果为空则返回,防止传入空指针

6.11const修饰成员函数

常函数:

  • 成员函数后加const后我们称这个函数为常函数
  • 常函数不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数

this指针的本质是指针常量,指针的指向是不可以修改的

void showPerson() const//this指针指向的值不可以更改,所加的const修饰的是this指针
{
    
}
mutable/*在常函数中可以修改*/ int m_B;

const Person P;//在对象前加const,不可修改

p.showPerson();//常对象只能调用常函数,不可以调用普通成员函数,因为普通成员函数可以修改属性

6.12友元

有些私有的属性也想让类外特殊的一些函数或者类进行访问

关键字 friend

6.12.1全局函数做友元

class Building
{
friend void goodGay(Building *building)//可以访Building类中的属性
public:
    Building()
    {
        m_SittingRoom="客厅";
        m_Bedroom="卧室";
    }
public:
    string m_SittingRoom;
private:
    string m_Bedroom;
}
//全局函数
void goodGay(Building *building)
{
    cout<< <<buliding->SittingRoom<<
    cout<< <<building->Bedroom<<//私有属性不可访问
}

写法:把全局函数前加friend放在类的最上面

6.12.2类做友元

class Building;
class GoodGay
{
    GoodGay();
public:
    void visit();//参观函数访问Building中的属性
    Building*building;
};
class Buidling
{
friend class GoodGay//goodgay可以访问本类私有成员
public:
    Building();
public:
    string m_SittingRoom;
private:
    string m_Bedroom;
};
//类外写成员函数
Building::Building()//Building下的构造函数
{
        m_SittingRoom="客厅";
        m_Bedroom="卧室";    
}
GoodGay::GoodGay()
{
    building=new Building;
}
void GoodGay::visit()
{
    cout<< <<building->SittingRoom<<
    cout<< <<building->Bedroom<<//私有不能访问
}

写法:把类前加上friend放在类前

6.12.3成员函数做友元

friend void GoodGay::visit()
void GoodGay::visit2()
{
    cout<<Bedroom;
}

写法:把成员函数前加上friend加到类前面

6.13运算符重载

6.13.1加号运算符重载

作用:实现两个自定义数据类型相加的运算

成员函数重载:

Person p3=p1.operator+(p2);

Person operator+(Person&p)
{
    Person temp;
    temp.m_A=this->m_A+p.m_A;
    temp.m_B=this->m_A+p.m_B;
    return temp;
}
vois test01()
{
    Person p3=p1+p2;
}

全局函数重载本质调用

Person p3=operator+(p1,p2)

Person operator(Person &p1,Person &p2)
{
    Person temp;
    temp.m_A=this->m_A+p2.m_A;
    temp.m_B=this->m_A+p2.m_B;
    return temp;
}

函数重载的版本

Person operator+(Person &p1.int num)
    {
    Person temp;
    temp.m_A=this->m_A+num;
    temp.m_B=this->m_A+num;
    return temp;
}

6.13.2左移运算符重载

可以输出自定义数据类型

成员函数:

void operator<<()//不会利用成员函数重载<<运算符,因为无法实现cout在左侧

全局函数:

ostream & operator<<(ostream &cout/*引用*/,Person &p)//简化cout<<p
{
   cout<<"m_A"<<p.m_A<<"m_B="<<m_B;
    return cout;
}

6.13.3递增运算符重载

重载前置++

MyInterger()
{
    m_Num=0;
}
Myinterger& operator++()
{
    //先进行++操作
    m_Num++;
    //再将自身返回
    return *this;
}

后置++

void operator++(int)//int 代表占位参数,可以用于区分前置和后置
{
    //先记录当时的结果
    MyInteger temp=*this;
    //递增
    m_Num++;
    //最后将记录的结果返回
    return temp;
}

6.13.4赋值运算符重载

c++编译器会给类添加赋值运算符operator=,对属性进行值拷贝。浅拷贝

Person& operator=(Person &p)
{
   //应该先判断是否有属性在堆区,如果有释放干净,再深拷贝
   if(m_Age!=NULL)
   {
       delete m_Age;
       m_Age=NULL;
   }
    m_Age=new int(*p.m_Age);
    return *this;//返回自身
}

6.13.5关系运算符重载

bool operator==(Person&p)
{
    if(this->m_Name==p.m_Name&&this->m_Age==p.m_Age)
    {
        return true;
    }
    return false;
}

6.13.6函数调用运算符重载

(),仿函数

class Myprint
{
public:
void orperator()(string test)
{
    cout<<test<<endl;
}
};
void test01()
{
    Myprint myprint;
    myprint("Hello world");//打印出hello world,使用重载的小括号
}
************************************
int operator()(int num1,int num2)
{
    return num1+num2;
}

匿名函数对象,当前行执行完可以被立即释放

cout<<MyAdd()(100,100)//匿名函数对象,重载小括号

6.14继承

6.14.1公共继承

class Java
{
public:
    void header()
    {
        //公共头部
    }
    void footer()
    {
        //公共底部
    }
    void content()
    {
        
    }
}
class pyhthon
{
public:
    void header()
    {
        //公共头部
    }
    void footer()
    {
        //公共底部
    }
    void content()
    {
        
    }    
}

继承实现

class BasePage
{
public:
void header()
    {
        //公共头部
    }
    void footer()
    {
        //公共底部
    }
};
class Java:public BasePage
{
public:
    void content()
    {
        
    }
}

好处:减少重复的代码

class 子类:继承方式 父类

子类:也称为派生类

父类:也称为基类

6.14.2保护继承

class B:protected A

{

};//私有权限访问不到

6.14.3私有继承

class B:private A

{

};//原public和protected变成private但原私有仍然无法访问

//如果C再继承B也访问不到A中的私有继承

6.14.4继承中的对象模型

public: int m_A;

protect:int m_B;

private: int m_C;//在父类中所有属性都会被子类继承,父类中私有的属性被隐藏,子类访问不到

6.14.5继承中的构造和析构顺序

先构造父类再构造子类

先析构子类再析构父类

6.14.6继承同名成员访问方式

同名成员属性:

直接访问子类

加作用域可以访问父类//s.Base::

同名成员函数:

直接调用子类

加作用域//s.Base::

调用函数重载:

如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数,如果想当问到父类中被隐藏的同名函数,要加作用域

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

同非静态一致

通过对象访问

s.m_A
s.Base::m_A
    s.func()
    s.Base::func()

通过类名当问

Son::m_A
Son::Base::m_A
    Son::func()
    Son::Base::func()
6.14.6.2多继承语法

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

父类中出现同名,加作用域。

6.14.6.3菱形继承
  • 两个派生类继承同一个基类
  • 又有某个类同时继承两个派生类
class Animal()
    class Sheep:public Animal()
        class Tuo:public Animal()
            class SheepTuo:public Sheep,public Tuo()

当菱形继承时,两个父类具有相同的数据,需要加以区分

Sheep::m_Age=18;

Tuo::m_Age=28;

利用虚继承 解决菱形继承问题

在继承之前加上关键字virtual变为虚继承

Animal类变为虚基类

两份数据共有一个,更改一次后就只有最后一个

viasual public Animal()

6.15多态

静态多态:函数重载,运算符重载

动态多态:派生类和虚函数实现动态多态(晚绑定,运行阶段确定函数地址)

在函数前加virtual

满足条件:有继承关系,子类要重写父类中的虚函数(函数返回值类型,函数名,参数列表完全相同)

6.15.1多态深刨析

vfptr虚函数指针

指向vftable虚函数表,表内部记录虚函数的地址Animal::speak

子类重写父函数,子类中的虚函数表内部会替换成子类的虚函数地址,当父类的指针或者引用指向子类对象的时候,会发生多态

6.15.2计算器类案例

#include<iostream>
#include<string>
using namespace std;
class Calculator
{//一般实现
public:
	int getResult(string oper)
	{
		if (oper == "+")
		{
			return m_Num1 + m_Num2;
		}
		else if (oper == "-")
		{
			return m_Num1 - m_Num2;
		}
	}
	int m_Num1;
	int m_Num2;
};
void test01()
{
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10;
	cout << c.m_Num1 << "+" << c.m_Num2 <<"="<<c.getResult("+")<< endl;
}


class AbstractCalculator
{
public:
	virtual int getResult()
	{
		return 0;
	}
	int m_Num1;
	int m_Num2;

};
class AddCalculator:public AbstractCalculator
{//利用多态实现
public:
	int getResult()
	{
		return m_Num1 + m_Num2;
	}
};
void test02()
{
	AbstractCalculator* abc = new AddCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1<<"+"<< abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}
  • 组织结构清晰
  • 可读性强
  • 对于前期和后期拓展以及维护性高

6.15.3纯虚函数和抽象类

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

当类中有了纯虚函数,那他就被称为抽象类

特点:

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

class Base{

virtual void func()=0;}

在test01中都无法实现Base b;

class Son::public Base

{

public:

virtual void func();//子类必须重写

}

6.15.4案例

#include<iostream>
using namespace std;
class AbstractDrinking

{
public:
	virtual void Boil() = 0;
	virtual void Brew() = 0;
	virtual void PourInCup() = 0;
	virtual void PutSomething() = 0;
	void makeDrink()
	{
		Boil();
		Brew();
		PourInCup();
		PutSomething();

	}

};
class Coffee :public AbstractDrinking
{
public:
	virtual void Boil()
	{
		cout << "a" << endl;
	}
	virtual void Brew()
	{
		cout << "b" << endl;
	}
	virtual void PourInCup()
	{
		cout << "c" << endl;
	}
	virtual void PutSomething()
	{
		cout << "d" << endl;
	}
};
class Tea :public AbstractDrinking
{
public:
	virtual void Boil()
	{
		cout << "A" << endl;
	}
	virtual void Brew()
	{
		cout << "B" << endl;
	}
	virtual void PourInCup()
	{
		cout << "C" << endl;
	}
	virtual void PutSomething()
	{
		cout << "D" << endl;
	}
};
void doWork(AbstractDrinking* abs)//一个接口有多个多态
{
	abs->makeDrink();
	delete abs;
}
void test01()
{
	doWork(new Coffee);
	doWork(new Tea);
}
int main()
{
	test01();
}

6.15.5虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。

共性:

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

区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象
virtual ~Base()
{
    
}
virtual ~Base()=0;

6.15.6案例

#include<iostream>
using namespace std;
class CPU
{
public:
	virtual void calculate() = 0;

};
class VideoCard
{
public:
	virtual void display() = 0;

};
class Memory
{
public:
	virtual void storage() = 0;

};
class Computer
{
public:
	Computer(CPU* cpu, VideoCard* vc, Memory* mem)
	{
		m_cpu = cpu;
		m_vc = vc;
		m_mem = mem;
	}
	void work()
	{
		m_cpu->calculate();
		m_vc->display();
		m_mem->storage();
	}
	~Computer()
	{
		if (m_cpu != NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_vc != NULL)
		{
			delete m_vc;
			m_vc = NULL;
		}
		if (m_mem != NULL)
		{
			delete m_mem;
			m_mem = NULL;
		}
	}
private:
	CPU* m_cpu;
	VideoCard* m_vc;
	Memory* m_mem;
};
class IntelCPU :public CPU
{
public:
	virtual void calculate()
	{
		cout << "intel" << endl;
	}
};
class IntelVideo :public VideoCard
{
public:
	virtual void display()
	{
		cout << "display" << endl;
	}
};
class IntelMemory :public Memory
{
		public:
			virtual void storage()
			{
				cout << "storage" << endl;
			}
};
void test01()
{
	CPU* intelCpu = new IntelCPU;
	VideoCard* intelCard = new IntelVideo;
	Memory* intelMem = new IntelMemory;
	Computer* computer = new Computer(intelCpu, intelCard, intelMem);
	computer->work();
	delete computer;
}
int main()
{
	test01();
	return 0;
}

7文件

通过文件可以将数据持久化

c++对文件操作需要包含头文件****(文件流)

文本类型:

  • 文本文件:以文本的ASCII码形式存储在计算机中
  • 二进制文件:文件以文本的二进制形式存储在计算机中

操作文件三大类:

  • ofsteam:“写”操作
  • ifstream:”读“操作
  • fstream:“读写”操作

7.1文本文件

1.包含头文件

#include

2.创建流对象

ofsteam ofs;

3.打开文件

ofs.open(“文件路径”.打开方式)

4.写数据

ofs<<“写入的数据”

5.关闭文件

ofs.close();

#include<iostream>
using nameapce std;
#include<fstream>
void test01()
{
	ofstream ofs;
	ofs.open("test.txt",ios::out)
	ofs<<"姓名:张三"<<endl;
	ofs<<"姓别:男"<<endl;
	ofs<<"年龄:18"<<endl;
	ofs.close();
}
int main()
{
	
	
} 

7.2文件打开方式

los::in:为读文件而打开文件

los::out:为写文件而打开文件

los::ate:初始位置:文件尾

los::trunc :如果文件存在先删除,再创建

los::libraray:二进制方式

文件打开方式可以配合使用:利用|操作符

7.3读文件

1.包含头文件

#include

2.创建流对象

ifsteam ifs;

3.打开文件

ifs.open(“文件路径”.打开方式)

4.读数据

四种方式读取

5.关闭文件

ifs.close();

#include<iostream>
using nameapce std;
#include<fstream>
#include<string>
void test01()
{
	ifstream ifs;
	ifs.open("test.txt",los::in);
	if(!ifs.isopen())//判断是否打开成功
	{
		
		cout<<"打开失败"<<endl;
		return;
	}
	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;		
	}
	ifs.close()
}
int main()
{
	
	
}

7.4二进制文件

打开方式指定为ios::binary

7.4.1写文件

ofstream write(const char* buffer,int len);

buffer指向内存中的一段读写空间

#include<iostream>
using nameapce std;
#include<fstream>
class Person
{
public:
	char m_Name[64];
	int m_Age;
};
void test01()
{
	ofstream ofs;
	ofs.open("person.txt",ios::out||ios::binary);
	Person p={"张三",18};
	ofs.write((const char*)&p,sizeof(Person));//用write函数以二进制的方式写数据
	ofs.close();
}
int main()
{
	
	
}

7.4.2读文件

istream& read(char* buffer,int len);

#include<iostream>
using nameapce std;
#include<fstream>
class Person
{
public:
	char m_Name[64];
	int m_Age;
};
void test01()
{
	ifstream ifs;
	ifs.open("person.txt",ios::in|ios::binary);
	if(!ifs.is_open())
	{
		cout<<""<<endl;
		return;
	}
	Person p;
	ifs.read((char*)&p,sizeof(Person));
	cout<<"姓名"<<p.m_Name<<endl;
	ifs.close();
}
int main()
{
	
	
}

C Primier Plus 笔记

内联函数

在这里插入图片描述

默认参数

在这里插入图片描述
在这里插入图片描述

函数模板

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值