本篇文章记录学习c++的这几日,c++其实就比c多了一点内容,如:类,继承,多态..面向对象的内容,如果你学习过C语言,学C++就非常简单,本文章写的比较简单,适合快速入门
文章目录
- 一、入门基本操作
- 概述
- 安装环境
- c++输入输出
- 数据类型
- 赋值运算,算数运算,关系运算,逻辑运算
- 二、分支结构
- if语句
- if嵌套
- 多条件if语句
- 三目运算符
- switch语句
- 三、循环结结构
- while语句
- do...while语句
- for语句
- 循环嵌套
- goto语句
- 四、函数
- 函数定义和声明
- 变量的作用域
- 函数参数传递
- 函数分文件编程
- 函数递归
- 五、指针
- 指针的概念
- 使用指针
- 指针作为函数的参数
- const修饰指针
- void类型指针
- 内存模型
- 动态分配内存new和delete
- 二级指针
- 函数指针和回调函数
- 六、数组
- 数组的概念
- 一维数组和指针
- 数组作为函数参数
- 动态创建一维数组
- 数组越界
- 二维数组
- 二维数组作为函数参数
- 七、结构体
- 结构体概念
- 结构体指针
- 结构体数组
- 结构体嵌套
- 简单链表
- 八、共用体
- 九、引用
- 引用的基本概念
- 引用作为函数参数
- 引用作为函数返回值
- 十、函数的重载
- 默认函数参数
- 函数重载、
- 十一、类
- 类的概念
- 类的使用
- 构造函数和析构函数
- 拷贝构造函数
- 浅拷贝和深拷贝
- 初始化参数
- const修饰成员函数
- this指针
- 静态成员变量
-
友元
- 重载运算符
- 继承
- 多态
一、入门基本操作
- 概述
c++语言是一门面向对象的编程,它的优点是容易维护,出现问题可以精确定位到。在C语言基础上的特征主要有:类及派生类、共有和私有成员、构造函数和析构函数、友元、内联函数、赋值运算符的重载,等等。
2.安装环境
因为我经常玩Linux,一般在Linux上面开发,先安装编译工具g++,在安装所需要的库就行了,你们也可以用其他环境,只要能编译c++代码就行了。
3.c++输入输出
输出
当前是没有声明缺省空间
#include <iostream> //头文件
int main()
{
std:cout <<"打印的内容";
std:cout <<"打印的内容"<<endl; //endl这个相当于是换行
return 0;
}
声明缺省空间,就可以进行简写
#include <iostream>
using namespace std;//声明缺省命名
int main()
{
cout << "打印的内容"<<endl;
return 0;
}
输入
#include <iostream>
using namespace std;//声明缺省命名
int main()
{
string name;//定义一个变量
cin >> name;//c++里面是不需要占位符的,可直接输出
cout << "name:"<<name<<endl;
return 0;
}
4.数据类型
c++常用的数据类型:整型,浮点型,字符型,字符串型(string),布尔类型,指针类型在
#include <iostream>
using namespace std;
int main()
{
//1.整型
int id;
//2.浮点型
float score; //其中有效位数为7位
double scores;//其中有效位数为15-16位
//3.字符型
char c; //存放到内存里面是ascll码,表示范围0-1297
//4.字符串型
string name="lijianhua";//用+号可以拼接字符串的内容
name = "姓名"+name+"作者";
//5.布尔类型
bool yz;//返回true/false,true=1,false=0;
//布尔类型本质就是一个整形可以用于相加
boo zz;
cout<<yz+zz;
return 0;
}
在c++如何表示二进制,八进制,十六进制?
二进制加上0b或0B,八进制加上0,十六进制加上0x
转义字符 \
在Ascll表中除了字符还有一些不能直接表示的特殊字符,比如: LF/NL,换行符,所以我们要有 \n 进行转义
强制类型转换
#include <iostream>
using namespace std;
//强制转换就是, 把一种数据类型转换成另外一种数据类型
int main()
{
int sum = 17, count = 5;
float mean;
mean = (flaot) sum / count;
cout<<mean;
}
5.赋值运算,算数运算,关系运算,逻辑运算
赋值运算
#include <iostream>
using namespace std;
int main()
{
int a,b,c;
a=b=c=2;//在赋值运算下,是从右自左运算
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
//赋值运算 +=,-=,*=,/=,%=;
c+=b;//表示c = c+b;依次类推,字符串只能用=赋值
}
算数运算
#include <iostream>
using namespace std;
int main()
{
//算数运算就是加减乘除取余
int a=4,b=2,tmp=0;
tmp = a+b;
cout<<"a+b="<<tmp'
tmp = a-b;
cout<<"a-b"<<tmp;
tmp = a*b;
cout<<"a*b="<<tmp;
tmp = a/b;
cout<<"a/b="<<tmp;
tmp = a%b;
cout<<"a%b"<<tmp;
//当多个算数运算一起的时候 是从 左到右依次计算(先乘除后加减),如果有括号,要先算括号里面的
int c=2;
tmp = a+b*c;
cout<<tmp;
return 0;
}
关系运算
#include <iostream>
using namespace std;
int main()
{
//关系运算有>,<,==,>=,<=,!=;一般使用关系运算用()括起来
int a=3,b=4;
bool tmp;
tmp = (a > b);
cout<<tmp;//如果a>b是真的,那就返回true,也就是1,依次类推如果 >=;
tmp = (a >= b);
cout<<tmp;
return 0;
}
逻辑运算
#include <iostream>
using namespace std;
int main()
{
//逻辑运算有 &&,||,!,使用用括号括起来
int a=3,b=4;
bool tmp;
tmp = (a && b);//两个表达式都是真的才返回true
tmp = (a || b);//只要1个表达式为真就返回true
tmp = (!a); //将当前状态进行取反,原本是真进行取反就变成了假
return 0;
}
二、分支结构
1. if语句
#include <iostream>
using namespace std;
int main()
{
int flag;
cout<<"1 == OPEN,0 == CLOSE"<<endl;
cin >>flag;
if(flag)
{
cout<<"打开成功"<<endl;
}
else
{
cout<<"关闭"<<endl;
}
return 0;
}
2. if嵌套
#include <iostream>
using namespace std;
int main()
{
int flag;
cout<<"1 == OPEN,0 == CLOSE"<<endl;
cin >>flag;
if(flag)
{
cout<<"打开成功"<<endl;
if(flag)
{
cout<<"谢谢"<<endl;
}
else
{
cout<<"滚"<<endl; }
}
}
else
{
cout<<"关闭"<<endl;
}
return 0;
}
3. 多条件if语句
#include <iostream>
using namespace std;
int main()
{
int flag;
cout<<"1 == OPEN,0 == CLOSE"<<endl;
cin >>flag;
if(flag)
{
cout<<"打开成功"<<endl;
}
else if(flag == 0)
{
cout<<"关闭"<<endl;
}
else
{
cout<<"您输入有误"<<endl;
}
return 0;
}
4. 三目运算
#include <iostream>
using namespace std;
int main()
{
int a=3,b=4,tmp=0;
tmp = a>b?a:b;//如果第一个表达式是真就把a赋给tmp,假就把b赋给tmp
cout<<tmp;
tmp = (a>b?a:b)>tmp?(a>b?a:b):b;
cout<<tmp;
return 0;
}
5.switch语句
#include <iostream>
using namespace std;
int main()
{
int tmp;
cout<<"请输入您的id"<<endl;
cin>>tmp;
switch(tmp)
{
case 1:
cout<<"这是id1"<<endl;
break;
case 2:
cout<<"这是id2"<<endl;
break;
default:
cout<<"输入有误"<<endl;
break;
}
//break用于打断离他最近的case或者while,for;
return 0;
}
三、循环结结构
- while语句
#include <iostream>
using namespace std;
int main()
{
int flag = 1;
while(flag <= 3) //表达式,然后在判断
{
cout<<"这是执行了第"<<flag++<<"次"<<endl;
}
return 0;
}
2.do..while语句
#include <iostream>
using namespace std;
int main()
{
int flag = 1;
do//运行do..while 至少会被执行一次
{
cout<<"执行第"<<flag++<<"次"<<endl;
}while(flag <= 3);
return 0;
}
3.for语句
#include <iostream>
using namespace std;
int main()
{
int i;
for(i =1;i<= 3,++i )
{
cout<<"执行第"<<i<<"次"<<endl;
}
return 0;
}
4.循环嵌套
#include <iostream>
using namespace std;
int main()
{
int i,j;
for(i =1;i<= 3;++i )
{
for(j=1;j<=2;++j)
{
cout<<"第"<<i<<"组的第"<<j<<"人"<<endl;
}
}
return 0;
}
5.goto语句
#include <iostream>
using namespace std;
int main()
{
int i =1;
cout<<"第一个"<<endl;
b:
cout<<"第三个"<<endl;
i++;
if (i <= 2)
goto b;
cout<<"第二个"<<endl;
//goto语句会跳转到你语句标号的那条语句去
return 0;
}
四、函数
- 函数的定义和声明
#include <iostream>
using namespace std;
int add(int a,int b);//函数声明,因为函数定义的时候在主函数后面,其作用域不一样,进行函数声
明,方可能访问到此函数
int main()
{
int max,a,b;
cin>>a b;
max = add(a,b);//函数的调用
cout<<max<<endl;
return 0;
}
int add(int a,int b)//函数的定义由函数返回的数据类型,函数名,函数的形参数据类型组成
{
if(a > b)
{
return a;
}
else if(b > a)
{
return b;
}
else
{
cout<<"a == b"<<endl;
}
}
2.变量的作用域
#include <iostream>
using namespace std;
int handler();
int b = 1;
int main()
{
//作用域有局部和全局,在语句块内部的变量成为局部变量,在函数外定义的变量成为全局变量
int a=2;
int b = 1;
handler();
cout<<"b="<<::b<<endl;//程序运行过后b=2,因为全局变量是所有函数都能访问到的变量
cout<<"a="<<a<<endl;//当前语句块能访问a变量
//如果全局变量和局部变量的变量名冲突了,我们想调用全局变量,就在变量名前加::
cout<<"局部变量="<<b<<endl<<"全局变量="<<::b<<endl;
//局部变量生命周期,函数返回,就释放 存储在栈区
//全局变量生命周期,程序结束,释放 存储在静态存储区
//static修饰静态变量,static默认只初始化一次,程序结束,生命周期才结束 存储在静态存储区
cout << handler();
return 0;
}
int handler()
{
static int c = 2; //不管函数调用几次,只会被初始化一次
c++;
cout<<"b ="<<b++<<endl;
//cout<<a;//当前函数访问不到a变量,因为是局部变量,只能在语句块它所定义的地方,才能访问
return c;
}
3.函数参数传递
#include <iostream>
using namespace std;
int handler(int a);
int main()
{
int a = 0;
handler(a);//传参的时候我们传的参数就是实参,而函数自己写的参数是形参
//函数只是把我们传的参数拷贝一遍,不管函数内如何改变参数的值,都不会影响到实参,
因为它只是一个复制品,能改变实参就要用到指针
cout<<a<<endl;
return 0;
}
int handler(int a)
{
a = 1000;
}
4.函数分文件编程
分文件编程,就是把函数所实现的模块代码按照不同的功能放到对应的文件中去,我们main函数需要那个功能,就添加对应的文件
#include <iostream>
#include "add.h" //添加对应的头文件
using namespace std;
int main()
{
int a,b,tmp;
tmp = add(a,b);
cout<<tmp;
return 0;
}
add.h
#include <iostream>
using namespace std;
int add(int a,int b);//添加函数声明
add.c
函数功能模块代码实现
#include "add.h"
int add(int a,int b)
{
if(a > b) return a;
else if (b > a) return b;
else return -1;
}
5.函数递归
函数递归就是函数自己调用自己,要有函数终止的条件,运用了栈的先进后出原理
#include <iostream>
using namespace std;
int add(int a);
int main()
{
int tmp;
tmp = add(100);
cout<<tmp;
return 0;
}
int add(int a)
{
if(a == 0) return 0;
return a+add(a-1);
}
五、指针
1.指针的概念
在c++中,一个变量会根据变量的数据类型来分配内存,我们如果想操作变量或者更改变量的值,那我们就必须知道变量的地址,进行操作,而指针就是存放普通变量的地址,指针会存放变量的起始地址
2.使用指针
#include <iostream>
using namespace std;
int main()
{
//取地址用&,取地址的值用*
int a = 3;
int *p = &a;
cout<<"a的地址是"<<&a<<endl<<"a的值="<<a<<endl;
cout<<"p存放的地址是"<<p<<endl<<"p指向地址存放的值是="<<*p<<endl;
*p = 4;
cout<<"更改后的值="<<a;
return 0;
}
3.指针作为函数的参数
#include <iostream>
using namespace std;
void handler(int *p);//指针作为形参,指向传过来的实参的地址,就可以改变实参的值
int main()
{
int a = 0;
handler(&a);
cout<<a;
return 0;
}
void handler(int *p)
{
*p = 1;
}
4.const修饰指针
#include <iostream>
using namespace std;
int main()
{
int a = 0;
int b = 0;
const int *p = &a;//常量指针,表示可以指向其他变量的地址,但是不能更改其变量的值
p = &b;
//*p = 2;//error
int * const p = &a;//指针常量,表示可以更改变量地址的值,但是不能重新指向其他变量的地址
*P = 1;
//p = &b;//error
const int * const p = &a;//既不能指向其他变量地址,也不能更改其他变量地址的值,只能引用
cout<<a;
return 0;
}
5.void类型指针
void类型不是没有,是任意类型指针,可以是int,char,float...,是一个不确定的指针,但是你用的时候强转成你用的指针就可以了
6.内存模型
内核空间
栈 :局部变量 函数的形参及返回值
堆 :存放动态分配的内存
数据段:存放全局变量 常量
代码段 :存放可执行的代码
模型依次是从下到上
7.动态分配内存new和delete
#include <iostream>
using namespace std;
int main()
{
int a = 0;
int *p =NULL;//为防止野指针
p=new int(3);//分配动态内存 并给分配的地址传入参数
cout<<"分配的地址:"<<p<<"值:"<<*p<<endl;
delete p;//动态分配的内存使用完 一定要进行删除 防止内存泄漏
return 0;
}
8.二级指针
#include <iostream>
using namespace std;
int main()
{
int a = 0;
int *p = &a;//一级指针指向普通变量的地址
int **q = &p; //二级指针指向指针变量的地址
cout<<"a的地址"<<&a<<endl<<"a的值:"<<a<<endl;
cout<<"p指向的地址"<<p<<endl<<"怕指向地址的值:"<<*p<<endl;
cout<<"q指向的地址"<<q<<endl<<"q指向的地址的地址"<<*q<<endl<<"q指向地址的地址值"<<**q;
return 0;
}
9.函数指针和回调函数
函数指针
#include <iostream>
using namespace std;
void handler(string *p);
int main()
{
string a = "lijianhua";
void (*p)(string *p);//函数指针本质上就是一个指针,只不过这个指针指向函数
//定义一个函数指针,要更函数一样的返回值,形参列表
p=handler;
p(&a);
return 0;
}
void handler(string *p)
{
cout<<*p<<endl;
}
回调函数
#include <iostream>
using namespace std;
void handler(void (*p)(int),int a);
void iverson(int a);
void lijianhua(int a);
int main()
{
int a = 1;
//回调函数就是一个函数作为另一个函数的参数,把自己变成一个参数,供调用其他函数
handler(lijianhua,a);
handler(iverson,a);
return 0;
}
void handler(void (*p)(int),int a)
{
p(a);
}
void lijianhua(int a)
{
cout<<"a="<<a;
}
void iverson(int a)
{
cout<<"a="<<a;
}
六、数组
1.数组的概念
数组存放的是相同的数据类型,是一块连续的内存,数组名保存的是第一个元素的地址,通过下标来访问数组的内容
2.一维数组和指针
#include <iostream>
using namespace std;
int main()
{
//数组的定义方式
int arry[3]={1,2,3};//定义同时赋值
int arry1[3]={0};
arry1[0] = 1;
arry1[1] = 2;
arry1[2] = 3;//先定义在赋值
int *p=NULL;
p = arry;//指向数组第一个元素的地址
for(int i=0;i<3;++i)
{
cout<<"第"<<i<<"个元素="<<arry[i]<<endl;//通过下标访问
cout<<"第"<<i<<"个元素="<<*(p+i)<<endl; //通过指针访问
}
return 0;
}
3.数组作为函数参数
#include <iostream>
using namespace std;
void Brry(int *p,int len);
void Arry(int a[],int len);
int main()
{
int arry[3]={1,2,3};
Arry(arry,3);
Brry(arry,3);
return 0;
}
void Arry(int a[],int len)//此时int a[] 占8个字节,相当于是一个指针
{
for(int i=0;i<len;++i)
{
cout<<"第"<<i<<"个元素="<<a[i]<<endl;
}
}
void Brry(int *p,int len)
{
for(int i=0;i < len;++i)
{
cout<<"第"<<i<<"个元素="<<*(p+i)<<endl;
}
}
4.动态创建一维数组
#include <iostream>
using namespace std;
int main()
{
int *p = new int[8];//表示分配8个元素的整型数组
for(int i=0;i<8;++i)
{
*(p+i) = i;//等同于p[i]
}
for(int i=0;i<8;++i)
{
cout<<"第"<<i<<"个元素="<<*(p+i)<<endl;
}
delete []p;//释放内存
return 0;
}
5.数组越界
因为数组的内存是连续存储的,假如我有3个元素,那对应这3个元素的地址都是连续的,我p指向了第一个元素,p+1就指向了下个元素的地址,当p+3的时候,就越界了,因为根本没有第四个元素
#include <iostream>
using namespace std;
int main()
{
int arry[3]={1,2,3};
int *p = &arry[1];
cout<<*p<<endl;
cout<<*(p+1)<<endl;
cout<<*(p+2)<<endl; //这块输出的是垃圾值,因为它越界了
return 0;
}
6.二维数组
#include <iostream>
using namespace std;
int main()
{
//二维数组的定义
int arry[2][3]={1,2,3,4,5,6};//可以这样赋值
int arry1[2][3]={{1,2,3},{44,5,6}};//第二种方法
int arry3[][3]={1,2,3,4,5,6};//可以不写行数
//二维数组也是块连续的内存,[2][3]从0行0列---1行2列,
int *p = (int *)arry;//这里要进行强转因为指向二位数组,应该用数组指针
for(int i=0;i < 6;++i)
{
cout<<"第"<<i<<"个元素="<<*(p+i)<<endl;
}
//也可以这样输出
for(int i=0;i<2;++i)
{
for(int j=0;j<3;++j)
{
cout<<"第"<<i<<"行"<<j<<"列="<<arry[i][j]<<endl;
}
}
return 0;
}
7.二维数组作为函数参数
#include <iostream>
using namespace std;
void Arry(int (*p)[4],int len);
int main()
{
int arry[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int i,j;
Arry(arry,3);
return 0;
}
//p 保存的是首地址,(*p)表示的是第0个元素的地址,*(*p),第0个元素地址的值
void Arry(int (*pr)[4],int len)
{
int i,j;
for(i = 0; i < len; i++)
{
for(j = 0; j < 4; j++)
cout<<*(*(pr+i)+j);
}
}
七、结构体
1.结构体概念
数组存放的是相同的数据类型,结构体存放的是不同的数据类型
#include <iostream>
#include <string.h>
using namespace std;
struct student
{
int id;
char name[12];
}s1;//可以在这里声明结构体变量
int main()
{
struct student s1;//也可以在这里声明结构体变量,也可以边定义边赋值
s1.id = 99;
strcpy(s1.name,"iverson");
cout<<"id="<<s1.id<<endl<<"name="<<s1.name<<endl;
//调用的时候用.进行调用,如果是结构体指针,就用->
return 0;
}
2.结构体指针
#include <iostream>
#include <string.h>
using namespace std;
struct student
{
int id;
char name[12];
char *p1;
};
int main()
{
struct student *p= new struct student[100];//动态分配内存
strcpy(p->name,"lijianhua");
p->id = 100;
cout<<"name="<<p->name<<endl<<"id="<<p->id<<endl;
p->p1 = (char *)"lijanhua";
return 0;
}
3.结构体数组
#include <iostream>
#include <string.h>
using namespace std;
struct student
{
int id;
char name[12];
};
int main()
{
struct student t1[3];//定义了三个结构体
for(int i=0;i<3;++i)
{
t1[i].id=i;
cin>>t1[i].name;
}
for(int i=0;i<3;++i)
{
cout<<"第"<<i<<"个元素="<<t1[i].id<<endl<<"第"<<i<<"个名字="<<t1[i].name<<endl;
}
return 0;
}
4.结构体嵌套
#include <iostream>
#include <string.h>
using namespace std;
struct teacher
{
char name[128];
};
struct student
{
int id;
struct teacher t1;
};
int main()
{
struct student t2;
t2.id=1;
strcpy(t2.t1.name,"Li teacher");
cout<<t2.id<<endl<<t2.t1.name<<endl;
return 0;
}
5.简单链表
链表用于存储离散的数据类型,我用的尾插法,一共分为两部分,数据域,和指针域,定义一个头指针和尾指针,尾指针始终指向新节点
#include <iostream>
using namespace std;
struct student* create();
int out(struct student *head);
struct student
{
int id;
struct student *next;
};
int main()
{
struct student *t1 = NULL;
t1 = create();
out(t1);
return 0;
}
struct student* create()
{
int len;
int values;
struct student *head = new struct student[256];
struct student *last = head;
last->next = NULL;
cout<<"请输入您要创建节点的个数"<<endl;
cin>>len;
for(int i=0;i<len;++i)
{
struct student *New = new struct student[256];
cout<<"请输入第"<<i+1<<"个节点的值:";
cin>>values;
New->id = values;
last->next = New;
last = New;
New->next = NULL;
}
return head;
}
int out(struct student *head)
{
struct student *p = head->next;
if(p == NULL)
{
cout<<"没有节点"<<endl;
return -1;
}
while(p != NULL)
{
cout<<p->id<<endl;
p = p->next;
}
}
八、共用体
共用体本质上就是共用一块内存,一次性只能输出一个变量的值,如果有多个变量被赋值,只会执行最后一个赋值的变量
#include <iostream>
#include <string.h>
using namespace std;
union student
{
int a;
char name[12];
};
int main()
{
union student t1;
t1.a =1;
strcpy(t1.name,"lijianhua");
cout<<t1.a<<endl;
cout<<t1.name<<endl;
return 0;
}
运行结果:
145112531
lijianhua
九、引用
1.引用的基本概念
引用就是取别名,和引用的变量共享一段内存,可以多个引用一个变量,但是不能空引用
#include <iostream>
using namespace std;
int main()
{
//引用的用法 数据类型 &变量 = 变量(必须是一样的数据类型)
int a=2;
int &ra = a;//给变量a取别名
cout<<a<<endl<<&a<<endl;
cout<<ra<<endl<<&ra<<endl;
return 0;
}
运行结果:
2
0x7ffe4dc69a1c
2
0x7ffe4dc69a1c
2.引用作为函数参数
#include <iostream>
using namespace std;
void handler(int &ra);
int main()
{
int a=2;
handler(a);
cout<<"a="<<a<<endl;
return 0;
}
//因为ra引用a,就是a的别名,这里ra就是a了,跟指针作用一样,可以改变实参的值
void handler(int &ra)//作用如同指针常量,int* const ra,可以改变其值,但是不能改变指向的变量
{
ra = 3;
}
运行结果:
a=3
3.引用作为函数返回值
#include <iostream>
using namespace std;
//把引用作为返回值
double arrs[5]={12.94,23.56,12.0,9.43,45.63};
double& setValues(int i)
{
return arrs[i];
}
int main()
{
//引用作为i返回值返回的是引用的变量本身,但是一定要全局变量或者static,否则由于作用域,变量会被提前回收
cout<<"改变前的值:"<<endl;
for (int i = 0; i < 5; i++)
{
cout<<"arrs["<<i<<"]:"<<arrs[i]<<endl;
}
//改变其中的两个元素
setValues(1)=11.11;
setValues(3)=33.33;
cout<<"改变后的值:"<<endl;
for (int i = 0; i < 5; i++)
{
cout<<"arrs["<<i<<"]:"<<arrs[i]<<endl;
}
}
~
十、函数重载
1.默认函数参数
函的形参在定义的时候,可以先赋值,我调用的时候,可以赋值的形参传参,也可以不传
#include <iostream>
using namespace std;
void handler(int a,string b ="lijianhua")
{
cout<<a<<endl<<b<<endl;
}
int main()
{
int a = 2;
handler(2,"dddd");
handler(2);
return 0;
}
2.函数重载
在定义函数的时候。可以用相同的函数名字,但是形参必须不一样(形参顺序也不能一样,有默认函数参数也不行)
#include <iostream>
using namespace std;
void handler(int &a,int &b)
{
int tmp;tmp = a;a = b;b = tmp;
}
void handler(string &a,string &b)
{
string tmp; tmp = a, a = b;b = tmp;
}
int main()
{
int a=1,b=2;
string c = "lijianhua";
string d = "iverson";
handler(a,b);
handler(c,d);
cout<<a<<endl<<b<<endl;
cout<<c<<endl<<d<<endl;
return 0;
}
十一、类
1.类的概念
我们可以理解为一个数据类型,这个数据类型是复合的,它既有数据又有函数。它和结构体的区别就是,结构体不能定义函数,而它可以
2.类的使用
#include <iostream>
using namespace std;
//声什么属性都不写 默认就是private
class student
{
public: //公共属性
int name;
void handler();//成员函数 由类变量调用
void handler(int a1,int b1);
private: //私有属性
int a;
int b;
};
int main()
{
class student t1;//定义一个类的变量
//也可以直接用类名定义变量 类名 变量
t1.handler(2,3);
t1.handler();
}
void student::handler()
{
cout<<a<<endl<<b<<endl;
}
void student::handler(int a1,int b1)//在类的外面定义 要用类名::
{
a = a1;
b = b1;
}
3.构造函数和析构函数
构造函数
构造函数在定义一个类的时候就有,如果程序员没有手动定义,编译器默认也会定义一个,一般用于承担初始化工作
#include <iostream>
using namespace std;
//构造函数就是用类名 形参组成,可以写多个构造函数,支持重载,也可以在类外面定义
//只有在创建类变量的时候才会调用构造函数,不能手动调用,但是可以直接调用
//必须是public权限
class student
{
public:
int name;
int score;
student();
student(int name1,int score1)
{
name = name1;
score = score1;
}
};
int main()
{
class student t1(2,99);//隐式调用
class student t2;//创建的时候不带括号 默认就调用没有形参的构造函数
t2.name = -1;
t2.score = -1;
cout<<t2.name<<endl<<t2.score<<endl;
cout<<t1.name<<endl<<t1.score<<endl;
class student t3 = student(2,3);//也可以这样调用,叫做显示调用,一般不这么调
}
student::student()
{
cout<<"这是地一个构造函数"<<endl;
}
析构函数
在开辟空间时,需要构造函数来开辟空间,对应,作用域退出时也需要一个函数做“收尾”的工作,与构造函数对应——析构函数
析构函数和构造函数作用相反,用于释放对象的内存空间
如果程序员未写构造函数,那么系统也会提供一个默认的构造函数,同样,如果程序员未写析构函数,那么系统也会提供一个默认的析构函数,在当前对象退出时,自动调用,进行释放空间。析构函数在对象生命周期结束后指向
#include <iostream>
using namespace std;
//一个构造函数仅此只要有一个析构函数
//析构函数语法 ~类名
class student
{
public:
int name;
int score;
student();
student(int name1,int score1)
{
name = name1;
score = score1;
}
~student();//析构函数也可以在类外面定义,
};
int main()
{
class student t2;
t2.name = -1;
t2.score = -1;
cout<<t2.name<<endl<<t2.score<<endl;
}
student::student()
{
cout<<"这是地一个构造函数"<<endl;
}
student::~student()
{
cout<<"这是一个系够函数"<<endl;
}
4.拷贝构造函数
同一类型的对象复制成员值来初始化对象,(当出现类的 “=” 赋值时,就会调用拷贝构造函数),拷贝构造函数如果没写,编译器会自动生成一个拷贝构造函数
#include <iostream>
#include <string.h>
using namespace std;
class student
{
public:
int id;
int *score;
student ()
{
cout<<"够找函数"<<endl;
}
student(student &s)//拷贝构造函数
{
id = s.id;
score = new int;
*score = *s.score;
cout<<"拷贝构造函数"<<endl;
}
~student()//delete堆内存
{
delete score;
score = NULL;
cout<<"系够函数执行"<<endl;
}
};
int main()
{
class student t1;
t1.id =99;
t1.score = new int(100);
cout<<t1.id<<endl<<*t1.score<<endl;
class student t2 (t1);//等价于 class student t2 = t1;
cout<<t2.id<<endl<<*t2.score<<endl;
}
~
5.浅拷贝和深拷贝
浅拷贝就是两个对象指向同一个内存,析构函数执行delete删除了内存,另一个对象就会出现错误
深拷贝就是两个对象分别指向不同的内存
#include <iostream>
#include <string.h>
using namespace std;
class student
{
public:
int id;
int *score;
student ()
{
cout<<"够找函数"<<endl;
}
student(student &s)
{
id = s.id;
score = new int;
*score = *s.score;
cout<<"拷贝构造函数"<<endl;
}
~student()
{
delete score;
score = NULL;
cout<<"系够函数执行"<<endl;
}
};
int main()
{
class student t1;
t1.id =99;
t1.score = new int(100);
cout<<t1.id<<endl<<*t1.score<<endl;
class student t2 (t1);//等价于 class student t2 = t1;
cout<<t2.id<<endl<<*t2.score<<endl;
}
6.初始化列表
初始化列表跟在构造函数后面语法是 : 参数(值),参数(值) ,它是在调用构造函数前就已经初始化了,解决了常量和引用,定义时必须要赋值
#include <iostream>
using namespace std;
class teacher
{
public :
int a;
const int b;//因为引用和常量必须在定义的时候就赋值,所以在程序执行的时候就会报错
int &c;
teacher(int a1,int b1,int &c1):a(a1),b(b1),c(c1)//而初始化列表是在调用构造函数前就已经初始化了
{
/*a= a1;
b = b1;
c = c1;*/
}
};
int main()
{
int arry =100;
class teacher t(1,2,arry);
cout<<t.a<<endl<<t.b<<endl;
cout<<t.c;
}
7.const修饰成员函数
#include <iostream>
using namespace std;
class student
{
public:
mutable int a;//mutable 可以突破不能访问const的
mutable int b;
student(int a1,int b1):a(a1),b(b1)
{
cout<<"this is my gouzhao handler"<<endl;
}
void handler() const; //const成员函数
~student();
};
student::~student()
{
cout<<"this is my xigou handler"<<endl;
}
void student::handler() const
{
//a = 100;编译错误,因为在const修饰函数过后,这个函数只能读这个对象的值,不能改写了
cout<<a<<endl<<b<<endl;
}
int main()
{
/*class student t1(100,100);
t1.handler();*/
const class student t2(200,200);//const 对象只能访问const成员函数,而非const对象可以访问任意函数
t2.handler();
}
8.this指针
this是c++中的一个关键字,表示当前对象,只能在类中使用,*this可以表述对象,因为this是一个指针,再用的时候要this->
#include <iostream>
using namespace std;
class student
{
public:
int a;
int b;
student(int a,int b)//解决重名的问题
{
this->a = a;//表示当前被调用的对象
this->b = b;
}
};
int main()
{
class student t1(100,100);
cout<<t1.a<<endl<<t1.b;
return 0;
}
9.静态成员变量
#include <iostream>
using namespace std;
//如果静态成员是私有属性,在类外访问将失败
class student
{
public:
char *name = NULL;
static int age;//定义静态变量的时候 还必须在类的外部定义一下
student(int age,char *name)
{
this->age = age;
this->name = name;
}
static void show()
{
cout<<"age="<<age<<endl;//静态成员函数只能访问静态变量 而非静态成员可以任意访问
}
};
int student::age = 1;//此时存储在静态常量去区,程序结束在释放类存
int main()
{
cout<<student::age<<endl; //在类外可以直接访问静态成员,但是要加类名::静态成员
student::show();
class student t1(20,"lijianhua");
t1.show();
return 0;
}
10.友元
在类中,访问问私有成员,只有通过共有属性进行访问,友元提供了一种方法,可以指定谁谁谁可以直接访问私有成员,友元函数,友元类,友元成员函数
using namespace std;
class boy;
class girl
{
public:
void show(boy &g);
void show2(boy &g);
};
class boy
{
friend void handler(boy &p);//friend定义了这个函数 表示这个函数就可以直接访问类里面的私有成员
//friend class girl;//friend定义了这个类,这个类就能访问boy的私有成员
friend void girl::show2(boy &g);//表示只有这个成员函数才能访问私有变量
friend void girl::show(boy &g);
private:
int bigD;
public:
int age;
boy(int a,int b):age(a),bigD(b)
{
}
};
void girl::show(boy &g)
{
cout<<"boy的bigD="<<g.bigD <<endl;
}
void girl::show2(boy &g)
{
cout<<"boy的bigD="<<g.bigD <<endl;
}
void handler(boy &p)
{
cout<<"bigD="<<p.bigD<<endl;
}
/*在friend成员函数的时候要讲究一个顺序,不声明,在调用的时候会报错
* 1.成员函数必须在类的外面定义,因为我们要frined
* 2.girl必须在要friend之前,不然由于先后顺序,会报错
* */
int main()
{
class boy t(20,18);
handler(t);
class girl t2;
t2.show(t);
return 0;
}
11.运算符重载
在c++中,函数重载就是,可以用相同的函数名(形参和顺序不同),而运算符重载,也是类似,用operator关键字进行重载,提高代码的可读性,让代码变得美观
#include <iostream>
using namespace std;
//运算符重载 在调用的时候 顺序不能变 返回值类型必须和接受的类型相同
class Tx
{
public:
Tx operator +(Tx &t);
bool operator==(Tx &t);//关系运算符重载
void show();//赋值运算符重载
Tx(int a):num(a)
{
}
private:
int num;
};
void Tx::show()
{
cout<<"num = "<<this->num<<endl;
}
Tx Tx::operator+(Tx &t)
{
this->num = this->num + t.num;
return *this;
}
bool Tx::operator==(Tx &t)
{
bool flag;
if (this->num == t.num)
{
flag = true;
}
else
flag = false;
return flag;
}
int main()
{
Tx t1(100);
Tx t2(200);
t1+t2;
t1.show();//300 //隐式传递,先传t1,在传t2
t2.show();//200
bool flag;
flag = t1 == t2;
cout<<flag<<endl;
return 0;
}
new和delete重载
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
void * operator new(size_t size);
void operator delete(void *p);
int num;
};
void * A::operator new(size_t size)
{
void *p = (void *)malloc(size);
cout<<"分配的内存地址"<<p<<endl;
return p;
}
void A::operator delete(void *p)
{
free(p);
cout<<"地址:"<<p<<"已经删除"<<endl;
}
int main()
{
A *t1 = new A ;
t1->num = 3;
printf("%p\n",t1);
printf("num = %d\n",t1->num);
delete t1;
return 0 ;
}
12.继承
继承就是我这类继承另一个类里面的成员,我就叫做派生类,被继承的类叫做基类,就是把共同的内容封装成一个类,减少了写代码的操作
#include <iostream>
using namespace std;
/**
在继承中,基类构造函数只能初始化基类,派生类构造函数只能初始化派生类
派生类在调用时,先调用基类的构造函数 在调用派生类的构造函数
* 退出的时候先调用派生类的析构函数,在调用基类的析构函数
* */
class animal
{
public:
char *name = NULL;
int age;
animal(int a):age(a)
{
}
private:
int sex;
};
class dog : public animal
/*以public继承基类,基类的公有成员就是派生类的共有成员,基类受保护的成员,就是派生类的受保护的成员,基类的私有成员,派生类不能访问
以private继承基类,继承的基类都是私有的 访问不了,基类的私有成员 也访问不了
*/
{
public:
int s;
void eat();
dog(int a,int b):animal(a),s(b)//这样可以同时初始化基类和派生类,但是基类必须有写anima构造函数,且只能用于同时初始化上一级,如果现在有一个c类,就初始化不了animal类,因为只能初始化继承上一级的类
{
cout<<"调用了dog构造函数"<<endl;
}
};
void dog::eat()
{
cout<<"够吃si"<<endl;
}
int main()
{
dog t1(18,1);
cout<<t1.age<<endl<<t1.s<<endl;
t1.eat();
return 0;
}
多继承语法:
class A
{
public:
int a;
};
class B
{
public :
int a;
};
class C : public A,pubic B
{
public :
int a;
};
如果当前类与继承的类变量或者函数重名,可以用域修饰符,
在调用的变量前面加入类名::
C t1;
t1.A::a;
t1.B::b;
t1.a;
13.多态
我继承了基类,和基类出现了同名函数,此时我基类只能访问基类同名函数,访问不了派生类同名的同名函数,但我现在把基类的同名函数设置成虚函数,我创建一个基类指针,指向派生类对象,此时编译器就会看你指向的是谁,就会调用对应的同名函数,引用也是同理
using namespace std;
class A
{
public :
virtual void show();
};
void A::show()
{
cout<<"this is A"<<endl;
}
class B:public A
{
public:
void show();
};
void B::show()
{
cout<<"this is B"<<endl;
}
class C:public A
{
public:
void show();
};
void C::show()
{
cout<<"this is C"<<endl;
}
int main()
{
A * t1 = NULL;
int flag;
cout<<"(1)访问A--(2)访问B---(3)访问C"<<endl;
cin>>flag;
switch(flag)
{
case 1:
t1 = new A;//指向哪个对象就访问该对象的同名函数
break;
case 2:
t1 = new B;
break;
case 3:
t1 = new C;
break;
default:
break;
}
if(t1 != NULL)
{
t1->show();
delete t1;
}
}