复习:
什么是引用?
别名
引用可以改变指向?
定义初始化 不可以改变指向
常引用怎么初始化
int a;
const int &ref= a
=100
=a+2
引用应用
形参
返回值
常引用做形参作用
保护实参不被修改
一、指针
回顾:
内存:
变量索引方式:
1.变量名
2.地址
定义指针
int*p
int *p;
int * p;
*p
*功能:
1.定义 --前边有类型
2.取值--前边没有类型
无类型指针---引用前 必须把它转为一种适当的数据类型
void *p;
新知识:
1.变量可能存在的空间:
1)堆:malloc-free new-delete
2)栈:作用域函数调用开始 变量释放作用域结束 int a int arr[3]
3)静态 与程序一样长生命周期
2.动态空间的申请和释放
c中 函数 malloc-free #include<cstdlib> malloc申请的内存空间是没有类型的void* 不会初始化
c++中 关键字 new-delete new申请的内存空间是有类型的 给内存初始化
3.动态空间的使用
c++类型检查更严格
#include
#include
using namespace std;
int main()
{
int *p=(int*)malloc(4);//错误 必须强转
}
实例:
#include<iostream>//input output stream 输入输出头文件
#include<cstdlib>
using namespace std;
int main()
{
int *p = NULL;
p =(int*)malloc(sizeof(int));
if(NULL==p)
{
cout<<"malloc failed"<<endl;
return 0;
}
*p=100;
cout<<*p<<endl;
if(p!=NULL)
{
free(p);
p = NULL;
}
}
1.变量申请动态空间
例1:
#include<iostream>//input output stream 输入输出头文件
using namespace std;
int main()
{
int *p=NULL;
//申请
p = new int;//申请一块整形空间 返回空间首地址
if(NULL==p)
{
cout<<"malloc error"<<endl;
return 0;
}
//使用
*p=666;
cout<<*p<<endl;
//释放
delete p;
p = NULL;//避免非法使用
}
例2:
#include<iostream>//input output stream 输入输出头文件
using namespace std;
int main()
{
int *p=NULL;
//申请空间并初始化
p = new int(666);//申请一块整型空间 并将666赋值进去 返回空间首地址
if(NULL==p)
{
cout<<"malloc error"<<endl;
return 0;
}
cout<<*p<<endl;
//释放
delete p;
p = NULL;//避免非法使用
}
练习:在main函数中动态开辟一个整型空间,并初始化为10,再赋值为20,最后注意内存的释放。
#include//input output stream 输入输出头文件
using namespace std;
int main()
{
int *p=NULL;
//申请空间并初始化
p = new int(10);//申请一块整形空间 并将666赋值进去 返回空间首地址
if(NULL==p)
{
cout<<"malloc error"<<endl;
return 0;
}
*p = 20;
cout<<*p<<endl;
//释放
delete p;
p = NULL;//避免非法使用
}
2.数组动态申请空间
例3:
#include<iostream>//input output stream 输入输出头文件
using namespace std;
int main()
{
int i;
int *p=NULL;
//申请
p=new int[3];//申请3块连续的整型空间 返回空间首地址 | | | |
if(NULL==p)
{
cout<<"malloc error"<<endl;
return 0;
}
//使用
for(i=0;i<3;i++)
{
p[i] = i+1;
}
for(i=0;i<3;i++)
{
cout<<p[i]<<" ";
}
//释放
delete []p;//释放连续空间
p = NULL;
}
3.动态空间初始化
例4:
#include<iostream>//input output stream 输入输出头文件
using namespace std;
int main()
{
int i;
int *p=NULL;
//申请
p=new int[3]();//申请3块连续的整型空间 将数组初始化为0 返回空间首地址 | | | |
if(NULL==p)
{
cout<<"malloc error"<<endl;
return 0;
}
//使用
for(i=0;i<3;i++)
{
p[i] = i+1;
}
for(i=0;i<3;i++)
{
cout<<p[i]<<" ";
}
//释放
delete []p;//释放连续空间
p = NULL;
}
练习:从键盘输入学生分数(int) 统计出最高分 最低分 平均分 要求:动态开辟空间
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
//申请空间
int* arr = new int[4];
if(NULL==arr)
{
cout<<"申请空间失败"<<endl;
exit(-1);
}
//赋值
for(int i = 0;i < 4;i++)
{
cin>>arr[i];
}
int max = arr[0];//记录数组中最大值
int min = arr[0];//记录数组中最小值
int sum = arr[0];//j记录数组中数据综合
for(int i = 1;i < 4;i++)
{
sum += arr[i];
if(arr[i] > max)
{
max = arr[i];//最大值更新
}
else if(arr[i]< min)
{
min = arr[i];//最小值更新
}
}
cout<<"max:"<<max<<" min:"<<min<<endl;
cout<<"avg:"<<sum/4<<endl;
delete []arr;
arr = NULL;
}
4.成员指针
#include
using namespace std;
struct date
{
int year;
int month;
int day;
};
int main()
{
date d={2022,3,15};
int date::*mp;//mp是成员指针
mp=&date::year;
//int date::*mp=&date::year
cout<<d.*mp<<endl;//d.year
date d2={2018,5,1};
cout<<d2.*mp<<endl;//d2.year
}
二、c++对c函数扩展
1、函数重载
理解:不同参数 相同用途
print(char)
print(int)
(1)含义:编程时 一个函数实现一个功能 有时要实现同一类函数的功能 只是细节不同
c++允许创建多个同名的函数(同一函数名 定义多个函数)–一物多用
(2)函数重载要求:形参列表必须不同(形参的类型不同或者形参的个数不同)
返回值无所谓
注意:如果形参列表相同 但返回值不同 是不可以重载
实例1:形参类型不同
#include<iostream>//input output stream 输入输出头文件
using namespace std;
void print(int a)
{
cout<<a<<endl;
}
void print(char a)
{
cout<<a<<endl;
}
int main()
{
int a=100;
char ch='x';
print(a);
print(ch);
}
实例2:形参个数不同
#include<iostream>//input output stream 输入输出头文件
using namespace std;
void print(int a,int b)
{
cout<<a<<endl;
cout<<b<<endl;
}
void print(int a)
{
cout<<a<<endl;
}
int main()
{
int a=100;
int d=123;
print(a,d);
print(a);
}
练习:函数重载实现求两个数据的和 数据可以都是int类型 也可以都是double类型
#include//input output stream 输入输出头文件
using namespace std;
void my_add(int a,int b)
{
cout<<(a+b)<<endl;
}
void my_add(double x,double y)
{
cout<<(x+y)<<endl;
}
int main()
{
int a=90,b=23;
double x=1.23,y=5.67;
my_add(a,b);
my_add(x,y);
}
总结:
1.重载规则:形参列表必须不同(不能因为函数不同的返回值进行重载)
2. 不提倡太多
注意说明:
关于函数重载,供参考:
1、C++对于字面值常数的默认类型规定
如 3.14默认为double 类型
那么,定义如下两个函数,
int fun(int n,int m);
float fun(float n,float m);
fun(10.0,2.5)就有歧义了 ---->解决:fun(10.0f,2.5f)
2、了解:
1)形参是引用、非引用时:
void func(int &x);
void func(int x);不属于重载
示例:
#include<iostream>
using namespace std;
void fun(int &num)
{
cout<<"num&..."<<num<<endl;
}
void fun(int num)
{
cout<<"int ..."<<num<<endl;
}
int main()
{
int num = 6;
fun(5);//可以调用整形的函数
fun(num);//编译报错 发生歧义
}
2)形参是 const 引用、非const 引用时:
void func(const int &x);
void func(int &x);属于重载。
3)形参是普通指针、指向常量的指针时:
void func(int * p) ;
void func(int const * p);属于重载。
4)形参是常指针、指向常量的指针时:
void func(int const *p);
void func(int *const p);属于重载。
5)形参是普通指针、指向常量的常指针时:
void func(int * p);
void func(int const * const p);属于重载。
6)形参使用了 typedef、没使用了 typedef时:
typedef unsigned int UINT
void func(UINT b);
void func(unsigned int x);不属于重载。
2、函数默认值
默认参数:函数调用时 形参可以给一个默认值 这时就不必从实参取值
实例1:如果实参传递值 则使用实参值 实参不传递值 则使用函数默认值
#include<iostream>//input output stream 输入输出头文件
#include<string>
using namespace std;
void set_color(string color="black")//型参默认值
{
cout<<color<<endl;
}
int main()
{
set_color();//如果实参不传递值 则使用函数默认值
set_color("red");//如果实参传递值 则使用实参值
}
实例2:形参默认值必须从右侧开始给
#include<iostream>//input output stream 输入输出头文件
#include<string>
using namespace std;
void set_color(int count,string color=“read”)//错 形参默认值必须从右侧开始给
{
cout<<count<<endl;
cout<<color<<endl;
}
int main()
{
set_color(8,"red");//如果实参传递值 则使用实参值
}
实例3:如果有函数声明 默认参数值要写到声明中
#include<iostream>//input output stream 输入输出头文件
using namespace std;
void add(int x=1,int y=2);//声明
int main()
{
//add(10,20);
add();
}
void add(int a,int b)
{
cout<<(a+b)<<endl;
}
练习:判断一个年份是否是闰年,声明中给默认年份
2016
year %4 == 0 && year %100 != 0 || year % 400 == 0
#include<iostream>//input output stream 输入输出头文件
using namespace std;
//函数声明
void judge(int year=2016);
int main()
{
judge();
judge(2022);
}
void judge(int year)
{
if(year%4==0&&year%100!=0||year%400==0)
{
cout<<"润年"<<endl;
}
else
{
cout<<"平年"<<endl;
}
}
3.内联函数
(1).定义 inline+函数名
(2).c++编译器会直接将内联函数函数体插入到函数调用的位置 所以内联函数没有普通函数跳转的额外开销(压栈,跳转,返回)
例子:
#include//input output stream 输入输出头文件
using namespace std;
inline void add(int m,int n)
{
cout<<(m+n)<<endl;
}
int main()
{
add(2,6);//函数调用
cout<<"end"<<endl;
}
(3).注意:
1)C++编译器不一定准许函数的内联请求!
内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型等)
内联函数是对编译器的一种请求,因此编译器可能拒绝这种请求
2)现代C++编译器能够进行编译优化,因此一些函数即使没有inline声明,也可能被编译器内联编译
(4).C++中内联编译的限制:
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作
函数内联声明必须在调用语句之前
编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。
因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
(5)带参宏与内联函数区别
1)带参宏
#include//input output stream 输入输出头文件
using namespace std;
#define MAX(a,b) (a)>(b)?(a):(b)
int main()
{
cout<<(MAX(3,7))<<endl;
}
2)区别
实例:带参宏实现
#include//input output stream 输入输出头文件
using namespace std;
#define MIN(a,b) (a)<(b)?(a):(b)
//(++a)<(b)?(++a):(b)
int main()
{
int a=1;
int b=3;
cout<<(MIN(++a,b))<<endl;//1. 3
cout<<"a:"<<a<<endl;//2. 3
cout<<"b:"<<b<<endl;//3. 3
}
实例:内联函数实现
#include//input output stream 输入输出头文件
using namespace std;
//#define MIN(a,b) (a)<(b)?(a):(b)
//(++a)<(b)?(++a):(b)
inline int min(int m,int n)//m 2 n 3
{
return m<n?m:n;
}
int main()
{
int a=1;
int b=3;
cout<<min(++a,b)<<endl;//1.2
cout<<"a:"<<a<<endl;//2. 2
cout<<"b:"<<b<<endl;//3.
}
三、面向对象
1、结构体
struct stu
{
int id;
char gender;
char name[20]
void sleep()
{
"呼呼";
“~~”
}
void eat()
{}
void class()
{
}
}
2、类
介绍对象
面向对象是c++核心
类–模型 对象是具体化的实例
编写对象的模型
class person
{
属性:
char gender;//性别
行为(方法)
void walk()
{
cout<<"pai~ pai~"<<endl;
}
};
(1)类使用步骤
1)声明类
class 类名
{
public:
成员变量(属性)
成员函数(行为/方法)
};
2)创建对象
类名 对象名
class person ps;//创建对象 也是实例化对象
3)调用类成员
.成员变量或者成员函数
->成员变量或者成员函数
实例:
#include<iostream>//input output stream 输入输出头文件
#include<string>
using namespace std;
//1.声明
class Person //class 是声明类的关键字 Person 类名
{
public:
string name;
char gender;
int age;
void init()
{
name="小美";
gender='f';
age=18;
}
void walk()
{
cout<<name<<((gender=='f')?"女孩":"男生");
cout<<"年龄: "<<age<<"岁"<<endl;
cout<<"跑:pai~pai~"<<endl;
}
void smile()
{
cout<<"笑:哈哈哈~~~~"<<endl;
}
};
int main()
{
//2.实例化
//class Person ps;//class可以省略
Person ps1;//实例化对象
//3.调用
ps1.init();//初始化person对象
ps1.walk();
ps1.smile();
}
练习:求圆的周长和面积
数据描述:
半径,周长,面积均用实型数表示
数据处理:
输入半径 r;
计算周长 = 2πr ;
计算面积 = π* r2 ;
输出半径,周长,面积;
• c语言编写:
#include<iostream.h>
using name std;
void main ()
{
double r, girth, area ;
const double PI = 3.1415 ;
cout << "Please input radius:\n" ; //操作符重载
cin >> r ; //输入
girth = 2 * PI * r ;
area = PI * r * r ;
cout << "radius = " << r << endl ;
cout << "girth = " << girth << endl ;
cout << "area = " << area << endl ;
}
要求:声明类 并实现以上功能
#include//input output stream 输入输出头文件
using namespace std;
#define PI 3.14
class Circle
{
public:
double r;//半径
void set_r(double radius)//double radius=3.0
{
r = radius;
//r=3.0;
//cout<<“请输入半径:”<<endl;
//cin>>r;
}
void getGirth()
{
cout<<"周长:"<<2*PI*r<<endl;
}
void getS()
{
cout<<"面积:"<<PI*r*r<<endl;
}
};
int main()
{
Circle c1;
c1.set_r(3.0);//调用成员函数
c1.getGirth();
c1.getS();
//Circle c2;
//c2.set_r();
//c2.getGirth();
}
(2)成员变量为数组
#include//input output stream 输入输出头文件
#include
using namespace std;
class Stu
{
public:
string name;
int age;
int score[3];
void init(string n,int a)
{
name=n;
age=a;
cout<<"请输入3科成绩:"<<endl;
for(int i=0;i<3;i++)
{
cin>>score[i];
}
}
void show()
{
cout<<"姓名:"<<name<<endl;
cout<<"年龄:"<<age<<endl;
cout<<"成绩:语文 "<<score[0];
cout<<" 英语:"<<score[1];
cout<<" 数学"<<score[2]<<endl;
}
};
int main()
{
Stu s;//实例化 s.score
s.init("小灰灰",2);
s.show();
Stu s1;//实例化 s1.score
s1.init("美女",18);
s1.show();
}
(3)成员变量为指针
#include//input output stream 输入输出头文件
#include
using namespace std;
class Stu
{
public:
string name;
int age;
//int score[3];
int *pscore;
void init(string n,int a)
{
name=n;
age=a;
//申请堆空间
pscore = new int[3];
if(NULL==pscore)
{
cout<<"malloc error"<<endl;
//退出程序 或者重复申请
}
cout<<"请输入3科成绩:"<<endl;
for(int i=0;i<3;i++)
{
cin>>pscore[i];
}
}
void show()
{
cout<<"姓名:"<<name<<endl;
cout<<"年龄:"<<age<<endl;
cout<<"成绩:语文 "<<pscore[0];
cout<<" 英语:"<<pscore[1];
cout<<" 数学"<<pscore[2]<<endl;
}
void destroy()
{
delete []pscore;
pscore = NULL;
}
};
int main()
{
Stu s;
s.init("小灰灰",2);
s.show();
s.destroy();
Stu s1;
s1.init("美女",18);
s1.show();
s1.destroy();
}
作业1:创建一个产品类product 成员变量自己定义(string name,int price) 使用set函数及get函数进行赋值 及获取结果(price)
在main中创建3个产品对象并赋值 打印
作业2:定义一个类 Array,定义二维数组成员,int arr[3][4];
重载函数print(),分别实现常规打印数组元素和以一定格式打印数组
数组初始化:init函数向二维数组中存入数据 如下:行下标i 列下标是j arr[i][j]=i+j
0 1 2 3
1 2 3 4
2 3 4 5
print() 函数打印3行4列的二维数组--常规格式
print(char sp);//sp可以是某个字符 打印输出如下
0#1#2#3
1#2#3#4
2#3#4#5
class Array
{
public:
int arr[3][4];
void init()//初始化数组
{
}
void print()//常规格式
{}
void print(char sp)//加#的打印
{
}
}