函数
。将经常使用的代码进行封装,来减少重复使用的代码
函数的定义
返回值类型 函数名(形参列表)
{
函数体语句;
return返回表达式
}
函数调用
#include<iostream>
using namespace std;
int xiangEjiaE(int zuoV,int youM)
{
int jieWguoV = zuoV + eyouM;
return jieWguoV;
}
int main()
{
int a =10;
int b =90;
//调用函数传入的值是实参
int c =xiangEjiaE(a,b);//函数调用
cout<<"C="<<c<<endl;
}
值传递
。函数调用的时候实参将数值传入给形参
。形参里发生的任何改变都不会影响实参
。如果函数不需要返回值,在声明的时候可以写void(无类型)
。。可以不写返回值return
函数的常见样式
无参无返
void wcwf()
{
cout<<"无参无反"<<endl;
}
有参无返
void ycwf(int a,int b)
{
cout<<a<<b<<endl;
}
无参有返
int wcyf()
{
return 100;
}
有参有返
int ycyf(int a,int b)
{
int jieWguoV = a+b;
return jieWguoV;
}
int mian()
{
int a =10;
int b =90;
int c =ycyf(a,b);
}
函数声明
。告诉编译器函数名称以及如何调用函数
。如果将函数新建在main函数后面就会报错,必须声明该函数
。函数声明的语法
int biVdaMxiaoV(int a,int b);//这是告诉编译器有这个函数的存在
。声明可以写多次但是定义只能有一次
分文件编写
。需要两个文件
。。同名.h的头文件
。。。在头文件里写同名源文件的声明
。。同名.cpp的源文件
。。。在源文件中写函数的定义
指针
指针的概念
。可以通过指针间接访问内存
。内存编号是从0开始记录的,一般用十六进制数字表示
。。0x000
。可以利用指针变量保存地址
。定义指针
数据类型 *指针变量
int a=100;
int *p=&a;
cout<<"a的地址"<<&a<<endl;
cout<<"通过指针输出a的变量值"<<*a<<endl;
。。&是取地址符,通过这个符号获取地址和输出地址
。。*是解引用,是用来输出指针指向的某个变量的变量值的
指针所占的空间
。指针也是一种数据类型
。在32位下占4字节
。。64位是8字节
cout<<"sizeof(int)"<<sizeof(int *)<<endl;
空指针
。空指针指针变量指向内存中编号为0的空间
。。用途:初始化指针变量
。。注意:空指针指向的内存是不可以访问的
。。。0到255之间的内存是系统占用的
。。。NULL是地址编号0
int *p =NULL;
野指针
。指针越界了
。指针变量指向非法的内存空间
//野指针
int *p = (int *)0x1110;//这不是系统自己分配的空间而是私自指向ed一个空间
const修饰指针
。const修饰指针三种情况
。。const修饰指针——常量指针
。。。常量指针的值可以修改,但是指向不可以修改
int a=10;
int b=20;
const int *p=&a;
*p =20;
p= &b;//会错误因为不能改变指向
。。const修饰常量——指针常量
int a=10;
int b=20;
int *const p=&a;//p是个变量
*p =20;
p =&b;//会发生错误不能修改指向
。。。指针的指向不可以更改
。。。指针指向的值可以更改
。。const即修饰指针又修饰常量
int a=10;
int b=20;
const int *const p=&a;
*p=20;//会发生错误
p=&b;//会发生错误
。。。指向与值都不允许更改
指针与数组
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int *p =arr;//首地址
cout<<*p<<ednl;//能用解引用的方式能解出来第一数据的首地址的变量
p++//移了四个字节每个数组的每一个int整形变量都是四个字节
int *p2=arr;
for(int i =0;i<10;i++)
{
cout<<p2<<ednl;
p2++;
}
指针与函数
。利用指针作为函数的形参可以影响实参的值
。指针传递的是地址不是值的传递
void diMzhiVchuanWdiM(int *p1,int *p2)
{
int jiaoVhuanM=*p1
*p1= *p2;
*2=jiaoVhuanM;
}
int main()
{
int a=10;
int b=20;
diMzhiVchuanWdiM(&a,&b);
cout<<a<<ednl;
cout<<b<<endl;
}
内存分区
。代码区:存放函数体的二进制代码,有操作系统进行管理
。全局区:存放全局变量和静态变量以及常量
。栈区:由编译器自动分配释放,在存放函数的参数,局部变量等
。堆区:由自己分配和释放,若自己不释放,只有在关闭程序之后由系统自动回收
。内存四区的意义
。。不同区域存放的数据,赋予不同的声明周期,能更大的灵活编程
new操作符
。new操作符是把变量创建在堆区
。需要自己手动释放
。。关键字:dlete
new的基本语法
int * hanWshuM()
{
//在堆区创建整形数据
int int *p = new int(10);
return p;
}
void ceMshiM
{
itn *p=hanWshuM;
cout<<*p<<endl;
//开辟在堆区的变量必须由自己来手动释放
delete p;
}
int main()
{
ceMshiM();
return 0;
}
在堆区利用new开辟数组
void ceMshiM()
{
int *arr = new int[10];
for(int i=0;i<10;i++)
{
arr[i] =i+100;//给这十个元素赋值100到109
}
for(int i=10;i<10;i++)
{
cout<<arr[i]<<endl;
}
}
int mian()
{
ceMshiM
return 0;
}
引用
。给变量起别名
。引用的本质在C++内部实现是一个指针常量
。语法
数据类型 &别名=原名;
int a=10;
int &b=a;//a和b都能操控同一个内存
。必须初始化
。不可改变
引用做函数参数
。函数传参时,可以利用引用的技术让形参修饰实参
。可以简化指针修改实参
。语法
void jiaoEhuanMhanWshuM(int &a,int&b)
{
int jiaoEhuanM=a;
a=b;
b=jiaoEhuanM;
}
int mian()
{
int a=10;
int b=20;
jiaoEhuanMhanWshuM(a,b);
}
引用做函数的返回值
。引用可以作为函数的返回值存在
。不要返回局部变量的引用
。。返回局部变量编译器只会保留一次
。。下一次就会把内存地址给了别的程序使用
。函数调用可以作为函数左值
。。那个函数返回谁就会直接把数据传给返回值
int &zuoVzhiW()
{
static int zuoV;
return zuoV;
}
int main()
{
//如果返回值是个引用,可以作为左值
zuoVzhiW()=1000;//把1000传给了返回值zuoV
return 0;
}
。static静态区的变量只有程序结束的时候才会被释放
常量引用
。常量引用主要用来修饰形参,防止误操作
int &a = 10;//引用不能使用常量的,必须是一块合法的内存空间
//加上const之后编译器会将代码修改 int temp =10; const int &a = temp;
const &a=10;//加上const之后就可以使用常量了
函数的默认参数
》在定义函数的时候可以为形参定义一些默认值这就是默认参数
》语法
》》返回值类型 函数名 (默认=默认值){}
//如果有参数默认值没有外部传入那么就等于默认参数值,如果有外部传入那么就不会使用默认参数值
int func(int a,int b=20,int c=30)
{
return a+b+c;
}
int main()
{
cout<<func(10)<<endl;
return 0;
}
》如果在某个位置已经有了默认参数,就从这个位置往后的位置都要有默认参数
》》这是错误的
int func(int a=1,int b,int c)
》》这是对的
int func(int a;int b=10,int c=20,int d=30)
》函数生命不可以有参数
函数占位参数
》C++中函数的形参列表里可以有站我参数,用来做站位,调用函数的时候必须填补该位置
》比如在食堂吃饭,去上厕所就把书包放在了那个位子上就表明这个地方已经有人占用了
//如果什么都不写只写一个数据类型int那么这个int就是占位符
//占位参数也可以有默认参数
void func(int a,int,int=10)
{
}
int main()
{
system("pause");
return 0;
}
函数重载
》函数名可以相同,提高复用性
》函数充值满足条件
》》同一个作用域下
》》函数名称相同
》》函数阐述类型不同或者个数不同或者顺序不同
void func()
{
cout<<"func的调用"<<endl;
}
void func(int a)
{
cout<<"func的调用"<<endl;
}
void func(double a)
{
cout<<"func的调用"<<endl;
}
int main()
{
func();//调用没有任何参数的func
func(10);//调用有参数的func
func(3.14)//调用有double的func函数
return 0;
}
》函数重载的返回类型如果不一样是不允许的
int func(int a);
void func(int a,int b)
函数重载的注意事项
》引用作为重载的条件
void func(int &a)
{
cout<<"func(int &a)调用"<<endl;
}
void func(const int&a)
{
cout<<"func(int &a)调用"<<endl;
}
int main()
{
int a=10;
func(a);//这个是调用的是没有const的
func(10);//这个调用的是带const的
return 0;
}
》函数冲宅碰到的默认参数
void func2(int a,int b,int c)
{
}
void func2(int a)
{
cout<<"func2(int a)"<<ednl;}
int main()
{
func2(10);
return 0;
}
》》这是不可以的第一种有三个参数的和第二个只有一个参数的都可以执行这样就会照成二义性的问题
类和对象
》面相对象的三大特征
》》封装
》》继承
》》多态
》C++中万物节课对象
》》对象有属性和行为
》》例如
》》》人可以作为对象
》》》》属性如:姓名、性别、年龄、身高、体重
》》》》属性如:行走、跑步、跳跃、吃饭、睡觉
》具有相同性质的对象,可以抽象称为类
封装
》封装的意义
》》将属性和行为作为一个整体,表现生活中的实物
》》将属性和行为加以权限控制
》语法
class 类名
{
访问权限:
属性
行为
};
》实例:
//圆周率
const double PI =3.16;
//class是类
class yuanWxingW
{
//权限
//public代表公共权限
public:
//属性
int banMjingM;
//行为
//获取圆的周长
double zhouEchangW()
{
return 2*PI*banMjingM;
}
};
int main()
{
//通过yuanWxingW类创建具体的圆(对象)
banMjingM yx1;
//属性赋值
yx1.banMjingM=10;
cout<<"圆的周长是多少"<<yx.zhouEchangW()<<endl;
system("pause");
return 0;
}
》类的关键字class
》类的大括号的最好要加上一个分号;
新关键字: class public
访问权限
》访问权限有三种
》》公共权限public
》》》成员在类内可以访问
》》》就是在类的作用域里面还是外面都可以访问
》》保护权限protected
》》》类内可以访问类外不可以访问
》》》儿子业可以访问父亲中的保护内容
》》私有权限private
》》》类内可以访问,类外不可以访问
》》》儿子不可以访问父亲的私有内容
class renWleiM()
{
public:
//公共权限
string xingMmingW;
protected:
//保护权限
string qiMcheE;
private:
//私有权限
int yinWhangWkaVmiMmaV;
public:
void func()
{
xingMmingW="张三";
qiMcheE="拖拉机";
yinWhangWkaVmiMmaV=123456;
}
};
int mian()
{
renWleiM rl1;
rl1.xingMmingW="李四";
rl1.qiMcheE="奔驰";//不能被修改访问
rl1.yinWhangWkaVmiMmaV=456789;//不能比修改访问
rl1.func();这个是可以被访问的
return 0;
}
新关键字 protected保护的 private 私有的
struct与class的区别
》默认的权限不同
》》struct默认权限为公共 class
》》class默认权限为私有 private
成员属性私有化
》优点
》》将所有成员属性奢姿私有,可以自己控制读写权限
》》对于写权限
class renWleiM
{
public:
//设置姓名
void setxingMmingW(string XingMmingW)
{
xingMmingW=XingMminggW;
}
//获取姓名
string getxingMmingW()
{
return xingMmingW;
}
//年龄保密只读
int getnianWlingW()
{
nianWlingW=0;
return nianWlingW;
}
private:
string xingMmingW;//姓名
int nianWlingW;//年龄
};
int main()
{
renWleiM rl1;
rl1.setxingMmingW("张三");
cout<<"姓名:"<<rl1.getxingMmingW()<<endl;
return 0;
}
》》可以检测数据的有效性
class renWleiM
{
public:
//读取年龄
int getnianWlingW()
{
nianWlingW=0;
return nianWlingW;
}
//设置带有判断的年龄
void setnianWlingW(string NianWlingW)
{
if(NianWlingW < 0||NianWlingW>150)
{
cout<<"输入的不合法"<<endl;
return;
}
nianWlingW=NianWlingW;
}
private:
int nianWlingW;//年龄
};
int main()
{
return 0;
}
对象的初始化和清理
》创建每个对象的时候都会有自己的初始化状态
》在结束的时候也会有清理数据的设置
构造函数与析构函数
》构造函数就是初始化
》析构函数就是清理
》构造函数与析构函数是系统自动调用的
》如果自己不提供构造函数和析构函数编译器会自己提供
》》编译器提供的构造函数与析构函数都是空实现
》构造函数:主要用于创建对象时为成员属性赋值,编译器自动调用
》析构函数:主要用于在销毁的时候系统会自动调用
》构造函数语法:
类名()
{
}
》》构造函数,没有返回值也不需要写void
》》函数名与类名一致
》》构造函数可以有参数,也可以发生重载
》》只要创建了对象那么程序在调用对象的时候会自动调用构造函数
》析构函数的语法:
~类名()
{
}
》》析构函数,没有返回值不用谢void
》》函数名与类名保持相同但是要加上波浪号~
》》析构函数不可以有参数,不能发生重载
》》程序在销毁前会自动调用
》代码实现
class renWleiM
{
public:
//构造函数
//无返回值不用写void
//与类名相同
//可以有参数和重载
//创建对象的一刹那就会被调用
renWleiM()
{
cout<<"renWleiM 构造函数的调用"<<endl;
}
//析构函数
//没有返回值不用谢void
//函数名前面要加上波浪号~
//不可以有参数
//对象销毁前会自动调用
};
int main()
{
return 0;
}
构造函数的分类
》有两种的分类方式
》》按照参数分为:有参构造和无参构造
》》按类型分为:普通构造和拷贝构造
//构造函数的分类
//分类
//按照参数分类 无参构造(默认构造)和有参构造
//安装类型分类 普通与拷贝构造
class renWleiM
{
//构造函数
public:
renWleiM()
{
cout<<"renWleiM的构造函数调用"<<endl;
}
~renWleiM()
{
cout<<"renWleiM的析构函数函数调用"<<endl;
}
renWleiM(int a)
{
cout<<"renWleiM的有参构造函数调用"<<endl;
}
//拷贝构造函数
//把括号里的同类拷贝过来
//在传进来的时候不能把传进来的原先值给改成其他的所以要加上const,以引用的方式传入
renWleiM(const nianWlingW &p)
{
xnianWlingW=p.nianWlingW;
}
int xingMmingW;
};
//调用
void diaoMyongM01()
{
//括号法
renWleiM p1(10);//有参构造函数
renWleiM p2(p1);//拷贝构造函数
//调用默认构造函数的时候不要加()
//编译器会任务是一个函数的声明
renWleiM p3();
//显示发
renWleiM p1;
renWleiM p2=renWleiM(10);//调用有参构造
renWleiM p3=renWleiM(p2);//拷贝构造
//等号右边的renWleiM()是匿名对象
//匿名对象在执行技术后会被机系统回收
//隐式转换法
renWleiM p4=10;//相当于renWleiM p4 =renWleiM(10);
renWleiM p5 =p4//这是拷贝构造调用
}
int main()
{
return 0;
}
拷贝函数的调用时机
》使用一个已经创建完毕的对象来初始化一个新的对象
》以值传递的方式给函数参数传值
》以值方式返回局部对象
class renWleiM()
{
public:
renWleiM()
{
cout<<"默认构造函数调用"<<endl;>
}
renWleiM(int nianWlingW)
{
cynianWlingM=nianWlingM;
}
renWleiM(const renWleiW &p)
{
cynianWlingW=p.cynianWlingW
}
~renWleiM()
{
}
int cynianWlingW;
};
//使用一个已经创建完毕的对象来初始化一个新的对象
void hsVceMshi1M()
{
renWleiM p1(20);
renWleiM p2(p1);
}
//以值传递的方式给函数参数传值
void hsVlinWshiWhanWshuM1(renWleiM p)
{
}
void hsVceMshiM2()
{
renWleiM p;
hsVlinWshiWhanWshuM1(p);//这里在调用拷贝构造函数
}
//以值方式返回局部对象
renWleM hslinWshiWhanWshuM2()
{
renWleiM p1;
cout<<(int*)&p1<<endl;
return p1;//根据p1来创建出一个新的对象
}
void hsVceMshiM3()
{
cout<<(int*)&p<<endl;
renWleiM p = hslinWshiWhanWshuM2();
}
int main()
{
return 0;
}
构造函数的调用规则
》默认情况下,编译器字少给一个类添加三个函数
》》某人构造函数(无参,函数体为空)
》》默认析构函数(无参,函数体为空)
》》默认拷贝构造函数,对属性值进行拷贝
》如果已经定义有参构造函数,C++就不在提供无参构造,还是会提供拷贝构造
》如果已经定义了拷贝够战术,C++不会再提供其他构造函数
class renWleiM()
{
public:
renWleiM()
{
cout<<"构造函数调用"<<endl;
}
renWleiM(int nianWlingW)
{
cynianWlingW=nianWlingW;
}
renWleiM(const renWleiM &p)
{
cout<<"拷贝构造函数调用"<<endl;
cynianWlingW=p.cynianWlingW;
}
~renWleiM()
{
cout<<"析构函数调用"<<endl;
}
int cynianWlingW;
};
void hsVceMshiM1()
{
renWleiM p
p.cynianWlingW = 18;
renWleiM p2(p);
cout << "p2的年龄为"<<p2.cynianWlingW<<endl;
}
void ceMshiM2()
{
renWleiM p;
}
int main()
{
hsceMshiM1();
hsceMshiM2();//如果写了有参构造那么系统就不在提供无参构造
return 0;
}
深拷贝与浅拷贝
》浅拷贝:简单的赋值拷贝操作
》深拷贝:在兑取重新申请空间,进行拷贝操作
》代码1
class renWleiM()
{
public:
renWlei()
{
cout<<"默认构造"<<endl;
}
renWlei(int nianWlingW,int shenEgaoE)
{
cyVnianWlingW=nianWlingW;
cyVshenEgaoE new int(shenEgaoE)
cout<<"有参构造"<<endl;
}
~renWlei()
{
//析构代码,将堆区开辟的数据做释放操作
if(cyVshenEgaoE !=NULL)
{
delete cyshenEgaoE;
}
cout<<"析构函数调用"<<endl;
}
int cyVnianWlingW;
int *cyVshenEgaoE;//打算把身高开辟到堆区
};
void ceMshiM1()
{
renWleiM p1(18,160);
cout<<"年龄"<<p1.cynianWlingW<<"身高"<<*p1.cyshenEgaoE<<endl;
renWleiM p2(p1);
cout<<"年龄"<<p2.cynianWlingW<<"身高"<<*p2.cyshenEgaoE<<endl;
}
int main()
{
return 0;
}
》new
是在堆区开辟内存空间需要自己手动释放
》析构代码将堆区开辟数据做释放操作
》释放堆区关键字delete
》如果利用编译器提供的拷贝构造函数,会做浅拷贝的操作
》》浅拷贝就是把一切都拷贝给另一个对象,就是什么都一样
》》int *cyshenEgaoE
代码主要问题出现在这句话里
》》》指针在堆区开辟一块内存存放的是160
》》》假设在堆区的编号是0x0011
》》》当做renWleiM p2(p1)
相当于把0x0011逐字节的没有改动,把它拷贝到p2当中
》》》p2中也记录着0x0011
》》》将析构代码的时候P1和P2都会执行析构
》》》》被释放过了第一次的时候是合法的第二次释放的时候就是非法的了
》》》》因为它们指向的都是同一块内存0x0011
》》》》浅拷贝带来的问题就是堆区的内存重复释放
》解决方法就是浅拷贝的问题要利用深拷贝进行剞劂
》》自己写一个拷贝构造函数
》》》自己写的拷贝函数有一个优点,那就是会自己使用独立的一块独立内存空间地址
》代码2
class renWleiM()
{
public:
renWlei()
{
cout<<"默认构造"<<endl;
}
renWlei(int nianWlingW,int shenEgaoE)
{
cyVnianWlingW=nianWlingW;
cyVshenEgaoE new int(shenEgaoE)
cout<<"有参构造"<<endl;
}
renWleiM(const renWleiM &p)
{
cout<<"拷贝构造函数的调用"<<endl;
cynianWling=p.cynianWlingW
//cyshenEgaoE=p.cynianWlingW这是编译器默认实现的就是这行代码
cyshenEgaoE =new int(*p.cyshenEgaoE);
}
~renWlei()
{
//析构代码,将堆区开辟的数据做释放操作
if(cyVshenEgaoE !=NULL)
{
delete cyshenEgaoE;
}
cout<<"析构函数调用"<<endl;
}
int cyVnianWlingW;
int *cyVshenEgaoE;//打算把身高开辟到堆区
};
void ceMshiM1()
{
renWleiM p1(18,160);
cout<<"年龄"<<p1.cynianWlingW<<"身高"<<*p1.cyshenEgaoE<<endl;
renWleiM p2(p1);
cout<<"年龄"<<p2.cynianWlingW<<"身高"<<*p2.cyshenEgaoE<<endl;
}
int main()
{
return 0;
}
初始化列表
》语法
构造函数():属性1(值1)属性2(值2)属性3(值3)……
{
}
》代码段
class renWleiM()
{
public:
renWleiM(int a,int b,int c):cya(a),cyb(b),cyc(c)
{
}
int cya;
int cyb;
int cyc;
};
int main()
{
return 0;
}
类对象作为类成员
》代码
#include<iostream>
#include<string>
using namespace std;
class shouVjiEleiM()
{
public:
shouVjiEleiM(string shouVjiEmingW)
{
cyshouVjiEmingW=shouVjiEmingW;
}
string cyVshouVjiEmingW;
};
class renWleiM()
{
public:
shouVjiEleiM(string mingWziM,string shouVjiEmingW):cyxingMmingW(mingWziM),cyshouVjiEmingW(shouVjiEmingW)
{
}
string cyVxingMmingW;
shouVjiEleiM cyVshouVjiEmingW
};
void ceMshiM1()
{
shouVjiEleiM a("小明","紫米");
cout<<a.cyxingMingW<<"拿着"<<a.cyshouVjiEmingW.cyVshouVjiEmingW<<endl;
}
int main()
{
ceMshiM1();
system("pause");
return 0;
}
静态成员
》静态关键字:static
》静态成员变量
》》所有对象共享同一份数据
》》在编译阶段分配内存
》》类内生命,类外初始化
》》代码
class renWleiM()
{
public:
//s所有对象都共享同一份数据
static int cyVa;
}
//类外初始化
int renWleiM::cya =100;
void ceMshiM()
{
renWleiM p1;
cout<<p.cya<<endl;
renWleiM p2;
p2.cya=200;
cout<<p.cya<<endl;//打印输出是200
}
int main()
{
ceMshiM1();
system("pause");
return 0;
}
》》》静态成员外部初始化操作int renWleiM::cya=100;
》》静态成员函数有两种两种访问方式
》》》通过对象进行访问
》》》通过类名进行访问
》》》代码
class renWleiM()
{
public:
//s所有对象都共享同一份数据
static int cyVa;
}
//类外初始化
int renWleiM::cya =100;
void ceMshiM2()
{
renWleiM p
//通过对象进行输出
cout<<p.cya<<endl;
//通过类名进行访问
cout<<renWleiM::cya<<endl;
}
int main()
{
ceMshiM2();
system("pause");
return 0;
}
》》私有静态成员外部是访问不到的
》静态成员函数
》》所有对象公用同一个函数
》》静态成员函数只能访问静态成员变量
》》代码
class renWleiM()
{
public:
static void func()
{
cout<<"static void func调用"<<endl;
}
};
//两种的访问方式
void ceMshiM1()
{
//通过对象访问
renWleiM p
p.func;
//通过类名访问
renWleiM::func();
}
int main()
{
system("puase");
return 0;
}
C++对象模型与this指针
成员变量和成员函数分开存储
》只有非静态成员变量才属于类的对象上
》代码1
class renWleiM()
{
};
void ceMshiM1()
{
renWleiM p;
cout<<"size of p="<<sizeof(p)<<endl;
}
int main()
{
return 0;
}
》》空对象占用内存为1字节
》》C++编译器会给空对象分配一个字节的空间,区分空对象占用内存的位置
》如果不是空的那就就按照那个变量类型进行划分相应的空间
this指针概念
》C++中成员对象与成员函数是分开存储的
》每一个非静态成员函数只会有一份函数实例
》this指针向被调用成员函数所属的对象
》》谁调用它就指向谁
》this指针隐含在每一个非静态成员函数内的一种指针
》》不需要定义,直接使用即可
》this指针的用途
》》当形参和成员变量同名的时候,可用this指针来区分
》》在类的非静态成员函数中返回对象本身,可以使用return *this
》代码1
class renWleiM()
{
public:
renWleiM(int nianWlingW)
{
this->nianWlingW=nianWlingW;
}
//如果要返回它的本体要以引用的方式作为返回
renWleiM& renWleiMjiaErenWleiM(renWleiM &p)
{
this->nianWlingW+=p.nianWlingW;
//this指向的是p2得到指针,而*this指向的就是p2这个对象本体
return *this;
}
int nianWlingW;
};
//解决名称冲突
void ceMshiM1()
{
renWleiM p1(1);
cout<<"p1的年龄为:"<<p1.nianWlingW<<endl;
}
//返回对象本身用*this
void ceMshiM2()
{
renWleiM p1(10);
renWleiM p2(10);
p2.renWleiMjiaErenWleiM(p1);
cout<<"p2的年龄为:"<<p2.nianWlingW<<endl;
//链式编程思想,可以无限往后加
p2.renWleiMjiaErenWleiM(p1).renWleiMjiaErenWleiM(p1).renWleiMjiaErenWleiM(p1).renWleiMjiaErenWleiM(p1).renWleiMjiaErenWleiM(p1).renWleiMjiaErenWleiM(p1);
cout<<"p2的年龄为:"<<p2.nianWlingW<<endl;
}
int main()
{
return 0;
}
》》如果renWleiMjiaErenWleiM
这个函数以值的方式来返回而不是引用
》》》p2.renWleiMjiaErenWleiM(p1)
这句话就会调用返回就不是本体而是拷贝构造函数的副本
const 修饰成员函数
》函数加上const关键字之后就称之为常函数
》》不能修改成员属性
》》如果仍要修改就要加上mutable
关键字
》代码1
class renWleiM()
{
public:
void daVyinMrenWleiM() const
{
this->cyVa =100;
}
//this的指向的值是可以修改的
//this指针就相当于const renWleiM * const this;
注1 void daVyinMrenWleiM()
{
this->cyVa=100;
}
注2 void daVyinMrenWleiM() const
{
this->cyVb=100
}
int cyVa;
mutable int cyVb;
};
void ceMshiM1()
{
//调用注2
const renWleiM a;
a.daVyinMrenWleiM();
}
int main()
{
return 0;
}
》》void daVyinMrenWleiM() const这条语句里不管是传入还是不传入,这里面都有一个this指针
》》》this指针的本质是一个指针常量
》》》this指针的指向是不可以修改的
》》》但是指向的值是可以修改的注意 void daVyinMrenWleiM()
》》》》前提没有带const关键字
》》写了void daVyinMrenWleiM() const
这个就相当于写了,void const renWleiM *const this
》》加了mutable关键字之后这个常量也就能修改了
》》常对象不能调用普通的成员函数
》常对象
》》生命对象前加const称该对象为常量对象
》》常对象只能调用常函数
友元
》家里有客厅(public)
》》客人可以进来
》自己的卧室(private)
》》只有自己允许的客人进来
》关键字:friend
》友元的三种实现
》》全局函数做友元
》》类做友元
》》成员函数做友元
全局函数做友元
》代码1
class jianMzhuMleiM()
{
public:
jianMzhuMleiM()
{
cyVkeMtingE="客厅";
cyVwoMshiM="卧室";
}
public:
string cyVkeMtingE;
private:
string cyVwoMshiM;
};
void haoVpengWyouV(jianMzhuMleiM *a)
{
cout<<"好朋友的全局函数正在访问"<<a->cyVkeMtingE<<endl;
cout<<"好朋友的全局函数正在访问"<<a->cyVwoMshiM<<endl;//错误的
}
void ceMshiM1()
{
jianMzhuMleiM b
haoVpengWyouV(b);
}
int main()
{
ceMshiM1();
return 0;
}
》代码2
class jianMzhuMleiM()
{
//haoVpengWyouV是jianMzhuMleiM的好朋友可以访问私有中的成员
friend void haoVpengWyouV(jianMzhuMleiM *a);
public:
jianMzhuMleiM()
{
cyVkeMtingE="客厅";
cyVwoMshiM="卧室";
}
public:
string cyVkeMtingE;
private:
string cyVwoMshiM;
};
void haoVpengWyouV(jianMzhuMleiM *a);
{
cout<<"好朋友的全局函数正在访问"<<a->cyVkeMtingE<<endl;
//在jianMzhuMleiM类中把haoVpengWyouV用friend关键字声明了所以可以访问私有权限了
cout<<"好朋友的全局函数正在访问"<<a->cyVwoMshiM<<endl;
}
void ceMshiM1()
{
jianMzhuMleiM b
haoVpengWyouV(b);
}
int main()
{
ceMshiM1();
return 0;
}
》》由于void haoVpengWyouV(jianMzhuMleiM *a);在jianMzhuMleiM类中用friend声明了因此可以访问私有权限
类做友元
》代码1
class jianMzhuMleiM()
{
friend class haoVpengWyouV;
public:
jianMzhuMleiM();
public:
string cyVkeMtingE;
private:
string cyVwoMshiM;
};
class haoVpengWyouV()
{
public:
haoVpengWyouV()
void canEguanEhanWshuM();//访问jianMzhuMleiM中的共有和私有的属性
jianMzhuMleiM *a;
};
//在外面写成员函数
jianMzhuMleiM::jianMzhuMleiM()
{
cyVkeMtingE="客厅";
cywoMshiM="卧室";
}
haoVpengWyouV::haoVpengWyouV()
{
a= new jianMzhuMleiM;
}
void haoVpengWyouV::canEguanEhanWshuM()
{
cout<<"好朋友正在访问:"<<jianMzhuMleiM->cyVkeMtingE<<endl;
cout<<
}
int main()
{
return 0;
}
》》可以在类外给类中的成员函数赋值
》》》在类外写jianMzhuMleiM::jianMzhuMleiM{里面写赋值语句}
》》在好朋友类的全局作用域中写入friend class haoVpengWyouV;
就能把其他的类视为好朋友就能访问了
》代码2注意问题
//告诉编译器有这个jianMzhuMleiM的类
class jianMzhuMleiM;
class haoVpengWyouV()
{
public:
jianMzhuMleiM * a;
};
成员函数做友元
》代码
class haoVpengWyouV
{
public:
haoVpengWyouV();
void fangVwenM();//让fangVwenM函数可以访问jianMzhuMleiM中私有的成员
jianMzhuMleiM *a;
};
class jianMzhuMleiM()
{
friend void fangVwenM();
public:
jianMzhuMleiM();
public:
string cyVkeMtingE;
private:
string cyVwoMshiM;
};
//类外实现成员函数
jianMzhuMleiM::jianMzhuMkeiM()
{
cyVkeMtingE="客厅";
cyVwoMshiM="卧室";
}
haoVpengWyouV::haoVpengWyouV()
{
a=new jianMzhuMleiM;
}
void haoVpengWyouV::fangVwenM()
{
cout<<"fangVwenM函数正在访问:"<<jianMzhuMleiM->cyVkeMtingE<<endl;
//如果想访问私有成员函数就必须要在那个类里写入friend void fangVwenM();这样就能访问私有权限了
cout<<"fangVwenM函数正在访问:"<<jianMzhuMleiM->cyVwoMshiM<<endl;
}
void ceMshiM()
{
haoVpengWyouV py;
py.fangVwenM();
}
int main()
{
ceMshiM();
return 0;
}
运算符重载
》为运算符重新进行定义,适应不同的数据运算
加好运算符重载
》假设两个自定义的类型编译器就不知道改怎么做了
》重载不如使用编译器所给的通用的关键字名字:operator+
》》使用operator+就可以简化为:renWleiM p3=p1+p2;
》代码1
//成员函数重载
class renWleiM
{
public:
renWleiM operator+(renWleiM &p)
{
renWleiM linWshiW;
linWshiW.cyVa=this->cyVa+p.cyVa;
linWshiW.cyVb=this->cyVb+p.cyVb;
return linWshiW;
}
int cyVa;
int cyVb;
};
void ceMshiM1()
{
renWleiM p1;
p1.cyVa=10;
p1.cyVb=10;
renWleiM p2
p2.cyVa=10;
p2.cyVb=10;
//成员函数重载的本质调用:renWleiM p3=p1.operator+(p2);
renWleiM p3= p1+p2;
cout<<"p3.cyVa="<<p3.cyVa<<endl;
cout<<"p3.cyVb="<<p3.cyVb<<endl;
}
int main()
{
ceMshiM1();
return 0;
}
》代码2
//全局函数重载+号
renWleiM operator+(renWleiM &p1,renWleiM &2)
{
renWleiM linWshiW;
linWshiW.cyVa=p1.cyVa+p2.cyVa;
linWshiW.cyVb=p1.cyVb+p2,cyVb;
return linWshiW;
}
void ceMshiM1()
{
renWleiM p1;
p1.cyVa=10;
p1.cyVb=10;
renWleiM p2
p2.cyVa=10;
p2.cyVb=10;
//全局函数重载本质调用:renWleiM p3=(p1,p2);
renWleiM p3= p1+p2;
cout<<"p3.cyVa="<<p3.cyVa<<endl;
cout<<"p3.cyVb="<<p3.cyVb<<endl;
}
int main()
{
return 0;
}
》代码3
//函数重载
renWleiM operator+(renWleiM &p1,int num)
{
renWleiM linWshiW;
linWshiW.cyVa=p1.cyVa+num;
linWshiW.cyVb=p1.cyVb+num;
return linWshiW;
}
void ceMshiM1()
{
renWleiM p1;
p1.cyVa=10;
p1.cyVb=10;
renWleiM p2
p2.cyVa=10;
p2.cyVb=10;
//运算符重载,也可以发生函数重载
renWleiM=p1+10;//renWleiM + int
}
int main()
{
return 0;
}
左移运算符重载
》左移运算符就是两个<<
小于号
》可以输出自定义的类型
》代码1
//左移运算符重载
class renWleiM
{
public:
int cyVa;
int cyVb;
};
//只能利用群居函数重载左移运算符
//cout属于ostream标准输出流类
//cout只有一个所以必须要用引用的方式
//这样就成功的变成了链式编程了
ostream &operator<<(ostream &cout,renWleiM &p)//本质是operator<<(cout,p)简化成:cout<<p
{
cout<<"cyVa="<<p.cyVa<<"cyVb="<<p.cyVb;
return cout;
}
void ceMshiM1()
{
renWleiM p;
p.cyVa=10;
p,cyVb=10;
cout<<p<<endl;//如果呀加endl就会触发链式编程
}
int main()
{
system("pause");
return 0;
}
》》通常不利用成员函数重载,因为无法实现cout在左侧
》建议写类的时候把成员属性设置为私有化
》》代码2
//左移运算符重载
class renWleiM
{
friend ostream &operator<<(ostream &cout,renWleiM &p);//友元声明
public:
renWleiM(int a,int b):cyVa(a),cyVb(b)
{
}
private:
int cyVa;
int cyVb;
};
ostream &operator<<(ostream &cout,renWleiM &p)//本质是operator<<(cout,p)简化成:cout<<p
{
cout<<"cyVa="<<p.cyVa<<"cyVb="<<p.cyVb;
return cout;
}
void ceMshiM1()
{
//因为私有化所以在外面就不能使用了
//可以提供一些接口但是这里不提
//必须要使用友元
//这里已经使用友元
//必须调用相关的构造函数
renWleiM p(10,10);
cout<<p<<endl;//如果呀加endl就会触发链式编程
}
int main()
{
system("pause");
return 0;
}
递增运算符重载
》代码1
class woVdeEzhengVshuM
{
friend ostream& operator<<(ostream& cout,woVdeEzhengVshuM zs);
public:
woVdeEzhengVshuM()
{
cyVbianMliangM = 0;
}
//前置++运算符
//重载前置++运算符,返回引用为了一直对一个数据进行递增操作
woVdeEzhengVshuM operator++()
{
cyVbianMliangM++;
//用自身作为返回
//this是指向自身的
//加*号做解引用
return *this;
}
private:
int cyVbianMliangM;
};
//重载左移运算符
ostream& operator<<(ostream& cout,woVdeEzhengVshuM zs)
{
cout<<zs.cyVbianVliang;
return cout;
}
void ceMshiM1()
{
woVdeEzhengVshuM zhengVshuM;
cout<<zhengVshuM<<endl;
}
int main()
{
ceMshiM1();
return 0;
}
》》重载前置++运算符,返回引用为了一直对一个数据进行递增操作
》》》这里很像构造拷贝函数
》代码2
class woVdeEzhengVshuM
{
friend ostream& operator<<(ostream& cout,woVdeEzhengVshuM zs);
public:
woVdeEzhengVshuM()
{
cyVbianMliangM = 0;
}
//重载后置++运算符
//operator++(int) int代表占位参数,可以用于区分前置和后置
//不用解引用的原因是相当于返回的是局部对象,在当前函数执行完就会自动被释放掉
woVdeEzhengVshuM operator++(int)
{
//先记录当时结果
woVdeEzhengVshuM linWshiWbianMliangM=*this;
//后递增
cyVbianVliangM++;
//最后将记录结果做返回
return linWshiWbianMliangM;
}
private:
int cyVbianMliangM;
};
//重载左移运算符
ostream& operator<<(ostream& cout,woVdeEzhengVshuM zs)
{
cout<<zs.cyVbianVliang;
return cout;
}
void ceMshiM2()
{
woVdeEzhengVshuM zhengVshuM;
cout<<zhengVshuM++<<endl;
cout<<zhengVshuM<<endl;
}
int main()
{
ceMshiM2()
return 0;
}
赋值运算符重载
》c++编译器至少给一个类添加4个函数
》》默认够战术(无参,函数体为空)
》》默认析构函数(无参,函数体为空)
》》默认拷贝构造函数,对属性进行值拷贝
》》赋值运算符 operator=,对属性进行值拷贝
》代码1
class renWleiM
{
public:
renWleiM(int nianWlingW)
{
//堆区的数据由自己手动开辟也需要自己手动释放
cyVnanWlnigW =new int(nianWlingW);
}
~renWleiM()
{
if(cyVnianWlingW !=NULL)
{
delete cyVnianWlingW;
cyVnianWlingW=NULL;
}
}
int *cyVnianWlingW;
};
void ceMshiM1()
{
renWleiM p1(18);
renWleiM p2(20);
p2=p1;//赋值的操作
cout<<"p1的年龄为:"<<p1.cyVnianWlingW<<endl;
cout<<"p2的年龄为:"<<p2.cyVnianWlingW<<endl;
}
int main()
{
return 0;
}
》》创建了两个对象p1与p2
》》p1里写了int *cyVnianWlingW
这是new出来的在堆区创建出来的
》》堆区存放的是18岁
》》》假设地址编号为0x0011
》》》指针就记录了0x0011
》》p2=p1他会把p1中的所有的数据作为一个简单的浅拷贝,逐个的都给拷贝到p2里去
》》》p2指向的也是0x0011内存地址
》》在析构的时候释放了两次
》》》重复释放导致释放不合法从而崩溃
》》解决方案
》》》利用深拷贝剞劂浅拷贝带来的问题
》代码2
class renWleiM
{
public:
renWleiM(int nianWlingW)
{
//堆区的数据由自己手动开辟也需要自己手动释放
cyVnanWlnigW =new int(nianWlingW);
}
~renWleiM()
{
if(cyVnianWlingW !=NULL)
{
delete cyVnianWlingW;
cyVnianWlingW=NULL;
}
}
//重载运算符
renWleiM& operator=(renWleiM &p)
{
//编译器提供的是浅拷贝cyVnianWlingW=p.cyVnianWlingW;
//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if(cyVnianWlingW !=NULL)
{
delete cyVnianWlingW;
cyVnianWlingW=NULL;
}
//深拷贝
cyVnianWlingW = new int(*p.cyVnianWlingW);
//返回对象本身
return *this;
}
int *cyVnianWlingW;
};
void ceMshiM1()
{
renWleiM p1(18);
renWleiM p2(20);
p2=p1;//赋值的操作
cout<<"p1的年龄为:"<<p1.cyVnianWlingW<<endl;
cout<<"p2的年龄为:"<<p2.cyVnianWlingW<<endl;
}
int main()
{
ceMshiM1();
return 0;
}
》代码3
class renWleiM
{
public:
renWleiM(int nianWlingW)
{
//堆区的数据由自己手动开辟也需要自己手动释放
cyVnanWlnigW =new int(nianWlingW);
}
~renWleiM()
{
if(cyVnianWlingW !=NULL)
{
delete cyVnianWlingW;
cyVnianWlingW=NULL;
}
}
//重载运算符
renWleiM& operator=(renWleiM &p)
{
//编译器提供的是浅拷贝cyVnianWlingW=p.cyVnianWlingW;
//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if(cyVnianWlingW !=NULL)
{
delete cyVnianWlingW;
cyVnianWlingW=NULL;
}
//深拷贝
cyVnianWlingW = new int(*p.cyVnianWlingW);
//返回对象本身
return *this;
}
int *cyVnianWlingW;
};
void ceMshiM1()
{
renWleiM p1(18);
renWleiM p2(20);
renWleiM p3(30);
p3=p2=p1;
}
int main()
{
ceMshiM1();
return 0;
}
》》renWleiM& operator=(renWleiM &p)
要返回引用不要返回值,返回值就相当于按照自身拷贝构造函数创建一个新的副本
》》》返回引用才是他真正的自身
》》return *this;
如果要找回自身需要有解引用*就是返回对象本身了
》》p3=p2=p1;
这样才是合法的
关系运算符重载
》代码1
class renWleiM
{
public:
renWleiM(string xingWmingW,int nianWlingW)
{
cyVxingWmingW =xingWmingW;
cyVnianWlingW =nianWlingW;
}
//重载 == 号
boll operator==(reiWleiM &p)
{
if(this->cyVxingWmingW==p.cyVxingWmingW && this->p.cynianWlingW ==p.niwanWlingW)
{
return true;
}
return false;
}
string cyVxingMmingW;
int cyVnianWlingW;
};
void ceMshiM()
{
renWleiM p1("张三",18);
renWleiM p2("张三",18);
if(p1 == p2)
{
cout<<"相等"<<endl;
}
}
int main()
{
return 0;
}
函数调用运算符重载
》函数调用运算符:()可以重载
》由于重载后使用的方法非常像函数的调用,隐藏成为仿函数
》仿函数没有固定写法,非常灵活
》代码1
class woVdeEdaVyinM()
{
public:
//重载函数调用运算符
void operatpr()(string shiWyanM)
{
cout<<shiWyanM<<endl;
}
}
void ceMshiM1()
{
woVdeEdaVyinM daVyinM;
daVyinM("你好");//由于使用非常类似于函数调用,因此称为仿函数
}
int main()
{
ceMshiM1();
return 0;
}
》代码2仿函数非常灵活没有固定的写法
//加法类
class woVdeEjiaEfaV
{
public:
int operator()(int a,int b)
{
return a=b;
}
};
void ceMshiM
{
woVdeEjiaEfaV jiaEfaV;
jiaEfaV(100,200);
cout<<"jiaEfaV="<<jiaEfaV<<endl;
}
int main()
{
ceMshiM();
return 0;
}
代码3
class woVdeEjiaEfaV
{
public:
int operator()(int a,int b)
{
return a=b;
}
};
void ceMshiM
{
//匿名函数对象
cout<<woVdeEjiaEfaV()(100,100)<<endl;
}
int main()
{
ceMshiM();
return 0;
}
继承
》定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性
继承的基本语法
》继承语法
》》class 子类: 继承方式 父类
》继承的好处就是减少重复代码
》代码1
class gongEgongMyangMshiM
{
public:
void V1()
{
cout<<"这里是第一"<<endl;
}
void V2()
{
cout<<"这里是第二"<<endl;
}
void V3()
{
cout<<"这里是第三"<<endl;
}
};
class jiMchengW1:public gongEgongMyangMshiM
{
public:
void duWteMdeE()
{
cout<<"独特的1"<<endl;
}
};
class jiMchengW2:public gongEgongMyangMshiM
{
public:
cout<<"独特的2"<<endl;
};
class jiMchengW3:public gongEgongMyangMshiM
{
public:
cout<<"独特的3"<<endl;
};
void ceMshiM()
{
cout<<"jiMchengW1如下:"
jiMchengW1 yi;
yi.V1();
yi.V2();
yi.V3();
jiMchengW2 er;
er.V1();
er.V2();
er.V3();
jiMchengW3 san;
san.V1();
san.V2();
san.V3();
}
int main()
{
return 0;
}
》子类也称派生类
》父类也称基类
继承方式
》继承的语法
》》class 子类:继承方式 父类
》继承的三种方式
》》公共继承
》》保护继承
》》私有继承
》代码1
class fuMleiM
{
public:
int Va;
protected:
int Vb;
private:
int Vc;
};
class ziVleiM: public fuMleiM
{
public:
//公共的属性
protected:
//保护的属性
private:
//不可访问fuMleiM的private:
};
class ziVleiM1: protected fuMleiM
{
public:
//继承fuMleiM的公共权限的内容变为保护权限
protected:
//保护的属性
private:
//不可访问fuMleiM的private:
};
class ziVleiM2: public fuMleiM
{
public:
//继承fuMleiM的公共权限都变为私有权限
protected:
//继承fuMleiM的保护权限业变为了私有权限
private:
//不可访问fuMleiM的private:
};
继承中的对象模型
》代码1
class fuMleiM
{
public:
int cyVa;
protected:
int cyVb;
private:
int cyVc;
};
class ziVleiM:public fuMleiM
{
public:
int cyVd;
}
void ceMshiM1()
{
//16
//父类中的所有非静态成员属性都会被子类继承下去
//父类中私有成员属性,是被编译器给隐藏了,因此访问不到,但确实是继承下去了
cout<<"size of ziVleiM"<<sizeof(ziVleiM)<<endl;
}
int main()
{
return 0;
}
继承中的构造和析构孙旭
》代码1
class fuMleiM
{
public:
fuMleiM()
{
cout<<"父类构造函数"<<endl;
}
~fuMleiM()
{
cout<<"父类构造函数"<<endl;
}
};
class ziVleiM:public fuMleiM
{
public:
ziVleiM()
{
cout<<"子类构造函数"<<endl;
}
~ziVleiM()
{
cout<<"子类构造函数"<<endl;
}
};
void ceMshiM()
{
ziVleiM a;
}
int main()
{
ceMshiM1();
return 0;
}
》继承中的构造和析构顺序
》》先构造父类,在构造子类
》》析构的顺序与构造的顺序相反
继承同名成员处理方式
》访问子类同名成员直接访问即可
》访问父类同名成员需要加作用域
》代码1
class fuMleiM
{
public:
fuMleiM()
{
cyVa=100;
}
int cyVa;
};
class ziVleiM:public fuMleiM
{
public:
ziVleiM()
{
cyVa=200;
}
int cyVa;
};
//同名成员属性处理
void ceMshiM()
{
ziVleiM s;
//如果出现重名直接访问,就是访问自身的ziVleiM中的cyVa
cout<<"ziVleiM下的cyVa="<<s.cyVa<<endl;
//如果通过子类对象访问父类中的同名成员加上父类的作用域s.fuMleiM::就行
cout<<"fuMleiM下的cyVa="<<s.fuMleiM::cyVa<<endl;
}
int main()
{
return 0;
}
》代码2
class fuMleiM
{
public:
fuMleiM()
{
cyVa=100;
}
void hanWshuM()
{
cout<<"fuMleiM的hanWshuM()调用"<<endl;
}
int cyVa;
};
class ziVleiM:public fuMleiM
{
public:
ziVleiM()
{
cyVa=200;
}
void hanWshuM()
{
cout<<"ziVleiM的hanWshuM()调用"<<endl;
}
int cyVa;
};
/*
void ceMshiM()
{
ziVleiM s;
//如果出现重名直接访问,就是访问自身的ziVleiM中的cyVa
cout<<"ziVleiM下的cyVa="<<s.cyVa<<endl;
//如果通过子类对象访问父类中的同名成员加上父类的作用域s.fuMleiM::就行
cout<<"fuMleiM下的cyVa="<<s.fuMleiM::cyVa<<endl;
}
*/
//同名的成员函数处理
void ceMshiM()
{
ziVleiM s;
s.hanWshuM();//直接调用ziVleiM里面的hanWshuM()
s.fuMleiM::hanWshuM();//调用fuMleiM里的hanWshuM()
}
int main()
{
return 0;
}
》代码3
class fuMleiM
{
public:
fuMleiM()
{
cyVa=100;
}
void hanWshuM()
{
cout<<"fuMleiM的hanWshuM()调用"<<endl;
}
void hanWshuM(int a)
{
cout<<"fuMleiM的hanWshuM(int a)调用"<<endl;
}
int cyVa;
};
class ziVleiM:public fuMleiM
{
public:
ziVleiM()
{
cyVa=200;
}
void hanWshuM()
{
cout<<"ziVleiM的hanWshuM()调用"<<endl;
}
int cyVa;
};
//同名的成员函数处理
void ceMshiM()
{
ziVleiM s;
s.hanWshuM();//直接调用ziVleiM里面的hanWshuM()
s.fuMleiM::hanWshuM();//调用fuMleiM里的hanWshuM()
//如果子类中出现了和父类同名得到成员函数,子类的同名成员会吟唱掉父类中的所有成员函数
//所有重载的版本全部隐藏掉
s.hanWshuM(10);
//想要访问父类中被隐藏掉的同名重载成员函数,需要加作用域
s.fuMleiM::hanWshuM(100);
}
int main()
{
return 0;
}
继承同名静态成员处理方法
》静态成员和非静态成员出现同名,处理方式一致
》》访问子类同名成员,直接访问
》》访问父类同名成员,需要加作用域
》代码1
class fuMleiM
{
public:
static int cyVa;
};
int fuMleiM::cyVa=100;
class ziVleiM:public fuMleiM
{
public:
static int cyVa;
}
int ziVleiM::cyVa=200;
//同名静态成员属性
void ceMshiM1()
{
//通过对象访问
cout<<"通过对象访问:"<<endl;
ziVleiM;
cout<<"ziVleiM下的cyVa="<<s.cyVa<<endl;
cout<<"fuMleiM下的cyVa="<<s.fuMleiM::cyVa<<endl;
//通过类名访问
cout<<"通过类名来访问"<<endl;
cout<<"ziVleiM下的cyVa="<<ziVleiM::cyVa<<endl;
cout<<"fuMleiM下的cyVa="<<ziVleiM::fuMleiM::cyVa<<endl;
}
int main()
{
ceMshiM1();
return 0;
}
》代码2
class fuMleiM
{
public:
static int cyVa;
static void hanWshuM()
{
cout<<"fuMleiM的static void hanWshuM()"<<endl;
}
};
int fuMleiM::cyVa=100;
class ziVleiM:public fuMleiM
{
public:
static int cyVa;
static void hanWshuM()
{
cout<<"ziVleiM的static void hanWshuM()"<<endl;
}
}
int ziVleiM::cyVa=200;
//通过静态成员函数
ceMshiM2()
{
//通过对象访问
cout<<"通过对象访问"<<endl;
ziVleiM s;
s.hanWshuM();
s.fuMleiM::hanWshuM();
//通过类名访问
cout<<"通过类名访问"<<endl;
ziVleiM::hanWshuM();
ziVleiM::fuMleiM::hanWshuM();
}
int main()
{
ceMshiM2()
return 0;
}
多继承语法
》语法
class 子类;继承方式父类1,继承方式 父类2……
》实际开发中不建议使用多继承
》代码1
class fuMleiM1
{
public:
fuMleiM1()
{
cyVa=100;
}
int cyVa;
};
class fuMleiM2
{
public:
fuMleiM1()
{
cyVb=100;
}
int cyVb;
};
class ziVleiM:public fuMleiM1,public fuMleiM2
{
public:
ziVleiM()
{
cyVc=300;
cyVd=400;
}
int cyVc;
int cyVd;
};
void ceMshiM()
{
cout<<"sizeof ziVleiM"<<sizeof(s)<<endl;
}
int mian()
{
ceVshiM();
return 0;
}
菱形继承
》菱形继承概念
》》两个派生类继承同一个基类
》》又有某个同事继承者两个派生类
》》这种继承被称为菱形继承,或者钻石继承
》菱形问题
》》羊继承了动物的数据,驼又同样继承了动物的数据,当羊驼使用数据的时候,就会产生二义性
》》》假设动物类中又cyVnianWlingW,羊与驼也会继承这份cyVnianWlingW,当羊驼继承自动物继承了两份,我们知道只需要一份就可以
》代码1
//动物类
class dongMwuM
{
public:
int cyVnianWlingW;
};
//羊类
class yangW:public dongMwuM{};
//驼
class tuoW:public dongMwuM{};
//羊驼
class yangWtuoW:public yangW,public tuoW{};
void ceMshiM1()
{
yangWtuoW yt;
yt.cyVnianWlingW=16;//这是错误的
yt.yangW::cyVnianWling=16;//这是可以的
yt.tuo::cyVnianWlingW=16;
//当出现菱形继承的时候有两份父类拥有相同的数据,需要加以作用域区分
cout<<"yt.yangW::cyVnianWling="<<yt.yangW::cyVnianWling<<endl;
cout<<"yt.tuo::cyVnianWlingW=16;"<<yt.tuo::cyVnianWlingW=16;<<endl;
//只有一份就可以,菱形继承导致数据有两份,资源浪费
}
int main()
{
return 0;
}
》》利用虚继承可以解决菱形继承的问题
》》关键字virtual
》》代码2
//动物类
class dongMwuM
{
public:
int cyVnianWlingW;
};
//利用虚继承virtual关键字
//羊类
class yangW:virtual public dongMwuM{};
//驼
class tuoW:virtual public dongMwuM{};
//羊驼
class yangWtuoW:public yangW,public tuoW{};
void ceMshiM1()
{
yangWtuoW yt;
yt.yangW::cyVnianWling=18;
yt.tuo::cyVnianWlingW=16;//这里改成16那么yt.yangW::cyVnianWling也会变成16
yt.cyVnianWlingW=16;//这样就不会发生错误了,因为数据只有一个
//当出现菱形继承的时候有两份父类拥有相同的数据,需要加以作用域区分
cout<<"yt.yangW::cyVnianWling="<<yt.yangW::cyVnianWling<<endl;
cout<<"yt.tuo::cyVnianWlingW=16;"<<yt.tuo::cyVnianWlingW=16;<<endl;
//只有一份就可以,菱形继承导致数据有两份,资源浪费
}
int main()
{
return 0;
}
多态
多态的基本概念
》多态分为两类
》》静态多态:函数重载:和运算符重载属于静态多态,服用函数名
》》动态多态:派生类和虚函数实现运行时多态
》静态多态和动态多态的区别
》》静态多态的函数地址早绑定,编译阶段确定函数地址
》》动态多态的函数地址晚绑定,运行阶段确定函数地址
》代码1
//多态
//动物类
class dongMwuM
{
public:
void shuoEhuaM()
{
cout<<"动物在说话"<<endl;
}
};
class maoEmiE:public dongMwuM
{
void shuoEhuaM()
{
cout<<"小猫在说话"<<endl;
}
};
//执行说话的函数
//地址早绑定,在编译极端就确定了函数的地址
void shuoEhuaMleE(dongMwuM &dw)//相当于dongMwuM &maoEmiE= MaoEmiE
{
dw.shuoEhuam();
}
void ceMshiM()
{
//创建的是猫传入函数体中的时候传入要求的是动物类
//相当于父类的引用再指向接受子类的对象
maoEmiE MaoEmiE;
shuoEhuaMleE(MaomiE);
}
int main()
{
ceMshim();
return 0;
}
》代码2
//多态
//动物类
class dongMwuM
{
public:
//需要加一个虚函数关键字:virtual
//加上虚函数之后这个类的内部家发生了改变
//当子类重新实现父类中同名的函数之后,他就可以实现地址的晚绑定了
virtual void shuoEhuaM()
{
cout<<"动物在说话"<<endl;
}
};
class maoEmiE:public dongMwuM
{
void shuoEhuaM()
{
cout<<"小猫在说话"<<endl;
}
};
class gouVgouV:public dongMwuM
{
public:
void shuoEhuaM();
{
cout<<"小狗在说话"<<endl;
}
};
//执行说话的函数
//地址早绑定,在编译极端就确定了函数的地址
//如果想执行猫说话,这个函数的地址就不能提前绑定,需要在运行阶段进行绑定,也就是地址栏晚绑定
/*动态多态满足条件
有继承关系
子类要重写弗雷德虚函数
*/
/*动态多态使用
父类的指针或引用,执行子类的对象
*/
void shuoEhuaMleE(dongMwuM &dw)//相当于dongMwuM &maoEmiE= MaoEmiE
{
dw.shuoEhuam();
}
void ceMshiM()
{
//创建的是猫传入函数体中的时候传入要求的是动物类
//相当于父类的引用再指向接受子类的对象
maoEmiE MaoEmiE;
shuoEhuaMleE(MaomiE);
gouVgouV xiaoVgouV;
shuoEhuaMleE(xiaoVgouV);
}
int main()
{
ceMshim();
return 0;
}
》》当使用虚函数的时候继承就可以实现晚绑定了
存函数与抽象类
》在多态中,通常弗雷中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
》纯虚函数的语法:virtua 返回类型 函数名{参数列表}=0;
》当类中有了纯虚函数,这个雷也称为抽象类
》抽象类的特点
》》无法实例化对象
》》子类必须重写抽象类中的纯虚函数,否则也属于抽象类
》代码1
class fuMleiM
{
public:
//纯虚函数
//只要有一个纯虚函数,这个雷成为抽象类
//抽象特点
//1无法实例化
virtual void hanWshuM() = 0;
};
void ceMshiM1()
{
fuMleiM a;//不允许使用抽象类对象
}
int main()
{
return 0;
}
代码2
class fuMleiM
{
public:
//纯虚函数
//只要有一个纯虚函数,这个雷成为抽象类
//抽象特点
//1无法实例化
//2抽象类的子类,必须重写弗雷中的纯虚函数,否则业属于抽象类
virtual void hanWshuM() = 0;
};
class ziVleiM:public fuMleiM
{
public:
virtual void hanWshuM(){};
}
void ceMshiM1()
{
ziVleiM s;//因为子类没有重写父类中的纯虚函数,也属于抽象类了
}
int main()
{
return 0;
}
代码3
class fuMleiM
{
public:
//纯虚函数
//只要有一个纯虚函数,这个雷成为抽象类
//抽象特点
//1无法实例化
//2抽象类的子类,必须重写弗雷中的纯虚函数,否则业属于抽象类
virtual void hanWshuM() = 0;
};
class ziVleiM:public fuMleiM
{
public:
virtual void hanWshuM()
{
cout<<"hanWshuM函数的调用"<<ednl;
};
}
void ceMshiM1()
{
ziVleiM s;//子类必须重写父类中的纯虚函数,否则无法实例化对象
fuMleiM *VfuMleiM=new ziVleiM;
VfuMleiM->hanWshuM();
}
int main()
{
return 0;
}
虚析构与纯虚析构
》多态使用的时候,父类的指针或引用来指向子类对象,父类对象是没有办法释放子类对象的析构代码的。
》如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
》解决方式:将父类中的析构函数改为虚析构或者纯虚析构
》虚析构和纯虚析构共性
》》可以解决父类指针释放子类对象
》》都需要有具体的函数实现
》虚析构和纯虚析构的区别
》》如果是纯虚析构,该类型属于抽象类,无法实例化对象
》虚析构语法
virtual ~类名(){}
》纯虚析构语法
virtual ~类名()=0;
》》类外
类名::~类名(){}
》虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
》如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
》拥有纯虚析构函数的类也属于抽象类
代码1
class dongMwuM
{
public:
dongMwuM()
{
cout<<"dongMwuM构造函数调用"<<endl;
}
~dongMwuM()
{
cout<<"dongMwuM析构函数调用"<<endl;
}
//纯虚函数
virtual void shuoEhuaM()=0;
};
class maoEmiE:public dongMwuM
{
public:
maoEmiE(string xm)
{
cout<<"小猫的构造析构函数"<<endl;
cyVxingmingW=new string (xm);
}
~maoEmiE()
{
if(cyVxingMmingW != NULL)
{
cout<<"小猫的析构函数调用"<<endl;>
delete cyVxingMmingW;
cyVxingMmingW=NULL;
}
}
//父类中的纯虚函数必须要在子类中重写,否则子类属于一个抽象类
virtual void shuoEhuaM()
{
cout<<*cyVxingMmingW<<"小猫在说话"<<endl;>
}
cout<<"小猫在说话"<<endl;
string *cyVxingMmingW;
};
void ceMshiM()
{
dongMwuM =dw=new xiaoVmao;
dw->shuoEhuaM();
delete dw
}
int mian()
{
ceMshiM();
return 0;
}
代码2
class dongMwuM
{
public:
dongMwuM()
{
cout<<"dongMwuM构造函数调用"<<endl;
}
//利用虚析构可以解决父类指针释放子类对象时不干净的问题
virtual ~dongMwuM()
{
cout<<"dongMwuM析构函数调用"<<endl;
}
//纯虚函数
virtual void shuoEhuaM()=0;
};
class maoEmiE:public dongMwuM
{
public:
maoEmiE(string xm)
{
cout<<"小猫的构造析构函数"<<endl;
cyVxingmingW=new string (xm);
}
~maoEmiE()
{
if(cyVxingMmingW != NULL)
{
cout<<"小猫的析构函数调用"<<endl;>
delete cyVxingMmingW;
cyVxingMmingW=NULL;
}
}
//父类中的纯虚函数必须要在子类中重写,否则子类属于一个抽象类
virtual void shuoEhuaM()
{
cout<<*cyVxingMmingW<<"小猫在说话"<<endl;>
}
cout<<"小猫在说话"<<endl;
string *cyVxingMmingW;
};
void ceMshiM()
{
dongMwuM =dw=new xiaoVmao;
dw->shuoEhuaM();
//父类的指针在析构的时候不会调用子类中析构函数,导致子类如果有堆区的属性会出现内存泄漏的情况
//解决问题非常简单,把析构改变成虚析构virtual ~dongMwuM()
delete dw
}
int mian()
{
ceMshiM();
return 0;
}
》代码3
class dongMwuM
{
public:
dongMwuM()
{
cout<<"dongMwuM构造函数调用"<<endl;
}
//纯虚析构
//这是一个声明而非实现 需要声明也需要实现
//有了纯虚析构之后,这个类业属于抽象类,无法实例化对象
virtual ~dongMwuM()=0;
//纯虚函数
virtual void shuoEhuaM()=0;
};
dongMwuM::~dongMwuM()
{
cout<<"dongMwuM纯虚析构函数调用"<<endl;
}
class maoEmiE:public dongMwuM
{
public:
maoEmiE(string xm)
{
cout<<"小猫的构造析构函数"<<endl;
cyVxingmingW=new string (xm);
}
~maoEmiE()
{
if(cyVxingMmingW != NULL)
{
cout<<"小猫的析构函数调用"<<endl;>
delete cyVxingMmingW;
cyVxingMmingW=NULL;
}
}
//父类中的纯虚函数必须要在子类中重写,否则子类属于一个抽象类
virtual void shuoEhuaM()
{
cout<<*cyVxingMmingW<<"小猫在说话"<<endl;>
}
cout<<"小猫在说话"<<endl;
string *cyVxingMmingW;
};
void ceMshiM()
{
dongMwuM =dw=new xiaoVmao;
dw->shuoEhuaM();
//父类的指针在析构的时候不会调用子类中析构函数,导致子类如果有堆区的属性会出现内存泄漏的情况
//解决问题非常简单,把析构改变成虚析构virtual ~dongMwuM()
delete dw
}
int mian()
{
ceMshiM();
return 0;
}
文件操作
》关键字包含头文件<fsftream>
》文件分为两种
》》文本文件:文件以文本的ASCLL码形式存储在计算机中
》》二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
》操作文件的三大类
》》ofstream
:写的操作
》》ifstream
:读的操作
》》fstream
:读写操作
文本文件
》写文件步骤
》》创建头文件
》》》#include<fstream>
》》创建流对象
》》》ofstream ofs;
》》打开文件
》》》ofsofs.open(文件路径,打开方式);
》》写数据
》》》ofs<<"写入的数据"
》》关闭文件
》》》ofs,close();
》打开方式
》》ios:.in 为读文件而打开文件
》》ios::out 为写文件而打开文件
》》ios:ate 初始位置:文件尾
》》ios:.app 追加方式写文件
》》ios:trunc 如果文件存在先删除,再创建
》》ios::binary 二进制方式
》注意:文件打开方式可以配合使用,利用|(未或)操作符
》例如:用二进制方式写文件 ios ::binary | ios:: out
》代码1
#include<stream>
void ceMshiM
{
//创建对象
ofstream ofs;
//制定打开打开方式
ofs.open("wenWbenV.txt",ios::out)
//写内容
ofs<<"姓名:张三"<<endl;
ofs<<"性别:男"<<endl;
ofs<<"年龄:18"<<endl;
//关闭文件
ofs.close();
}
int main()
{
return 0;
}
读文件
》读文件步骤如下
》》包含头文件
》》》#include <fstream>
》》创建流对象
》》》ifsTream ifs;
》》打开文件并判断文件是否打开成功
》》》ifs.open(“文件路径",打开方式);
4.
》》读数据
》》》四种方式读取
》》关闭文件
》》》ifs.close();
#include<fstream>
#include<string>
void ceMshiM()
{
ifstream ifs;
//打开文件,并且判断是否打开
ifs.open("wenWbenV.ttxt",ios::in)
if (! ifs.is_open());
{
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;
}
//第四种
char c;
shile((c = ifs,get())!=EOF)//EOF是文件尾部的标准
{
cout<<c;
}
//关闭文件
ifs.clse();
}
int main()
{
ceMshiM();
return 0;
}
二进制文件
写文件
》二进制方式写文件主要利用流对象调用成员函数write
》函数原型:ostream& write(const char *buffer,int len);
》参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
》代码
#include<fstream>
class renWleiM
{
public:
char cyVxingMmingW[64];//C语言的数组
int cyVnianWlingW;
}
void ceMshiM()
{
ofstream ofs;
//打开文件
ofs.open("person.txt",ios::out|ios::binary);
//写文件
renWleiM r={"张三",18};
ofs.write((const char )*&p.sizeof(renWleiM));
//关闭文件
ofs.close();
}
int main()
{
ceMshiM();
}
读文件
》二进制方式读文件主要利用流对象调用成员函数read
》函数原型:istream& read(char *buffer,int len);
》参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
》代码
#include<fstream>
class renWleiM
{
public:
char cyVxingMmingW[64];
int cyVnianWlingW;
};
void ceMshiM()
{
//创建流对象
ifstream ifs;
//打开文件 判断文件是否打开成功
ifs.open(renWleiM.txt,ios::in|ios::binary);
if(!ifs.is_open())
{
cout<<“文件打开失败”<<endl;
return ;
}
//读文件
renWleiM r;
ifs.read((char *)&r,sizeof(renWleiM););
cout<<"姓名:"<<r.cyVxingMmingW<<"年龄:"<<r.cyVnianWlingW<<endl;
//关闭文件
ifs.close();
}
int main()
{
ceMshiM();
return 0;
}