在C中要想输入和输出 我们会经常用到
#include <stdio.h>
在C++中头文件的命名风格不用.h
#include <iostream> using namespace std;
c++中 为什么要写<iostream>和using namespace std;-CSDN博客
这俩个函数是在std命名空间下定义的
#include <iostream>
using namespace std;
int main() {
int a;
cin>>a;
cout << "a的数值是:" << a << endl;
return 0;
}
#include <iostream>
int main() {
int a = 10;
std::cin >>a;
std::cout << "a的数值是:" << a << std::endl;
return 0;
}
ceil()函数向上取整
round( )四舍五入取整
#include <iostream>
using namespace std;
int main() {
int a = 10;
cout<<ceil(3/2.0)<<endl;
return 0;
}
c语言没有 是C++中的一种基本数据类型(内置数据类型)
只用一个字节
非零的数组都是true
一块有类型的内存
我们在C语言中在堆区申请内存空间是用malloc 释放用free
如果不释放会造成这段内存但用以后无法被访问 造成内存的泄露
在c++用我们new 开辟堆区空间 delete释放堆区空间
#include <iostream>
using namespace std;
int main() {
//int* p = (int*)malloc(sizeof(int));C语言中
int* p = new int;
return 0;
}
new与malloc相同的是 都可以返回开辟这段空间的首元素的地址
都是不同点是 new返回的地址类型是自动匹配存储信息的内容的,而malloc是返回的空类型需要强制类型转换
#include <iostream>
using namespace std;
int main() {
//int *p=(int*)malloc(sizeof(int)*4);
int* p = new int[4] {1,2,3,4};//在堆区中开辟了4个int这么大小的空间 并返回了它的首元素的地址
for (int i = 0; i < 4;i++) {
cout << p[i] << " ";
}
return 0;
}
注意在开辟空间的时候初始化 在c中用malloc是不可以的,但是C++的new可以实现这个功能
中间是没有等号的
如果不初始化里面的数据是随机数
delete
#include <iostream>
using namespace std;
int main() {
//int *p=(int*)malloc(sizeof(int)*4);
int* p = new int[4] {1,2,3,4};//在堆区中开辟了4个int这么大小的空间 并返回了它的首元素的地址
for (int i = 0; i < 4;i++) {
cout << p[i] << " ";
}
delete[]p;//把p所指向的那段空间都释放了
//delete p//只是把那段空间的第一个元素释放了,与free()不同
//如果不是放p所指向堆区的那段空间 会造成内存的泄露
p=NULL;
return 0;
}
#include <iostream>
using namespace std;
int main() {
//int *p=(int*)malloc(sizeof(int)*4);
int* p = new int(3);//初始化1个数据
return 0;
}
在C++语法上来看,引用是给变量起了个别名,不占用空间
类型& 引用变量名 =引用实体
引用是给变量起别名,那前提肯定得有实体变量
引用是给变量起别名,给空起了个别名没意义
这与引用的实质有关系
引用的实质是一个指针常量,是一个指向方向固定的指针,所有它不能更改指向,也就不能改变引用关系
int& b=a;
int* const b=&a;
#include <iostream>
using namespace std;
int main() {
int a = 5;
int& b3;//引用必须初始化
int& b2 = NULL;//引用不能初始化为空
int& b1 = a;
int* const b1 = &a;//本质 -->指针常量(指向不能改变,但是指向内存空间里面的内容可以改变)n
cout << b1 << " " << *b2;
return 0;
}
可寻址、有变量名的值
不可寻址、一般没有变量名的值
int a=10;//a为左值 有变量名 可寻址(&x)可以找到地址
int& b=a;
#include <iostream>
using namespace std;
int main() {
int&& c = 10;//10为右值 不可寻址(&10错误)
return 0;
}
const 类型 引用名 =引用实体
int main() {
const int a = 10;
const int& b = a;
const int& c = 10;
return 0;
}
值传递(只是一个传值的过程),但是传递的值可能是地址,也可能是基本类型数据
引用传递
#include <iostream>
using namespace std;
void text1(int a,int b) {
int t;
t = a;
a = b;
b = t;
}//值传递
void text2(int*a,int*b) {
assert(a);
assert(b);
int t;
t = *a;
*a = *b;
*b = t;
}//址传递
void text3(int& a,int& b) {//int& a=a; int& b=b;
int t;
t = a;
a = b;
b = t;
}//引用传递
void text4(int* const a,int* const b) {//int* const a=&a; int* const b=&b;
int t;
t = *a;
*a = *b;
*b = t;
}
int main() {//引用做函数的形参 可以修改传递过来实参变量的值
int a = 5;
int b = 10;
text1(a, b);//值传递
text2(&a,&b);//址传递
text3(a,b);//引用传递
text4(&a,&b);
return 0;
}
指针要判空的,但是引用不用 可以避免很多麻烦
在内存中不会产生返回值的副本
1.引用必须进行初始化,但是指针可以不初始化,为野指针
2.引用不用初始化为空,但是指针可以初始化为空,为空指针
3.引用与变量的关系不能改变,但是指针的指向确可以改变
4.引用所占的空间大小为 类型大小所对应的字节,而指针4/8Byte
#include <iostream> using namespace std; int main() { int a = 10; int& b = a; int* c = &a; cout<<"引用的大小:" << sizeof(b)<<endl<<"指针变量的大小:"<<sizeof(c)<<endl; return 0; }
5.没有多级引用,但是有多级指针,二级三级指针
6.引用的++是指别名所在的那段空间数据的++,而指针的++是指向的移动
函数调用,是主调函数向被调函数传值,然后被调函数返回结果给主调函数的一个过程
这个过程是需要函数栈来辅助的
栈是向下生长的,就是由高地址向低地址开辟空间
堆是向上生长的,就是由低地址向高地址开辟空间
形参在入栈的时候是从右向左入栈的,而实参传值给形参的时候是从左向右传值(相当于出栈的顺序)
#include <iostream>
using namespace std;
int func(int a = 1, int b = 2, int c = 3) {//函数形参的默认参数
cout << "你好" << endl;
}
int func1(int a = 1, int b, int c = 3) {
cout << "OK" << endl;
}
int main() {
func(3, 2, 1);
//预期:30 20 10
func1(2, 3);
return 0;
}
剖析下上面程序的执行过程当函数执行到主函数func位置时,会由函数的调用,主调函数调用被调函数,被调函数会调用一个新的函数栈,被调函数的形参从右向左入栈,一次是c、b、a,然后主调函数向被调函数传值,按照从左到右的顺序传值
原因:主函数中你认为值2会传给b但是实际上 他是按从左到右的顺序去传递的 2被赋值给了a b没有被赋值 所以会报错
#include <iostream> using namespace std; int func1(int a = 1, int b, int c = 3) { cout << "OK" << endl; } int main() { func1(2); return 0; }
C中函数就是由函数名组成的
C++函数是由函数名+参数组成的
也就是说C++中参数不同但函数名相同的函数是不同的函数,而C语言里只要函数名一样就是相同函数了
所以C语言中不存在在函数的重载
在调用相同函数名的函数的时候,通过参数列表的不同来却分到底进入哪个函数
参数列表的不同可以是参数个位的不同,参数顺序的不同,参数类型的不同等等
#include <iostream>
using namespace std;
void fun(int a, int b) {
cout << 1 << endl;
}
void fun(int a, float b) {
cout << 2 << endl;
}
void fun(double a) {
cout << 3 << endl;
}
void fun(float a) {
cout << 4 << endl;
}
int fun(int a, double b) {
return 5;
}
//int fun(float a) {
// cout << 5 << endl;
//}//报错了 因为函数的重载就函数名相同参数不同 返回值对函数重载的判断不起作用
void fun(int a, int b, int c) {
cout << 5 << endl;
}
int main() {
fun(1, 1);
fun(1, 1.1f);
cout << fun(1, 1.1)<<endl;
fun(1.1);
fun(1.1f);
return 0;
}
#include <iostream>
using namespace std;
void func(int &b) {
cout << "1" << endl;
}
void func(const int &b) {
cout << "2" << endl;
}
void func(int&& b) {
cout << "3" << endl;
}
int main() {
int a = 2;
func(a);
func(2);
return 0;
}
引用分为左值引用 右值引用 和 万能引用
可以用这个来作为函数参数的不同
观看下面的示例,确实符合函数重载的定义(函数名相同,参数列表不同可以重载),但是仔细观察第一个函数是有一个默认参数的,理论上它传递2个值就可以。而第二个函数就是传递俩个参数,这就会造成编译器无法确定你到底要将这俩个值传递给谁
#include <iostream>
using namespace std;
void func(int a,int b,int c=1) {
cout << "1" << endl;
}
void func(int a,int b) {
cout << "2" << endl;
}
int main() {
func(1,2);
return 0;
}
#include <iostream>
using namespace std;
void func(int a, int b) {
cout << "1"<<endl;
}
int func(int a, int b) {
cout << "2" << endl;
}
int main() {
func(1, 1);
return 0;
}
编译器无法仅根据返回值的类型来判断是否能够重载
1.结束条件
2.前进
3.递归的调用
4.回退
int func(){
if();//递归的结束条件
//前进
func();//递归的调用
//回退
}
为什么斐波那契数列能用递归
能写出递归树
比如你想算第五项的斐波那契数列
你要想求5 就必须得知道4和3 你要想知道4和3 就必须得知道2和1 你要想知道 2和 1 就必须知道1 和 0 而1 和0 我们还真知道 所以符合递归
递归树如图:
int Fb(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
int ret=Fb(n-1)+Fb(n-2);
return ret;
}
int main() {
Fb(5);
return 0;
}
int Tj(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
int ret=Tj(n-1)+Tj(n-2);
return ret;
}
int main() {
Tj(5);
return 0;
}
递归树如图:
# 1.由struct类过度到类
我们在C语言中都学过struct类型 他是一种复合类型 里面可以定义各种基本类型的变量 但是不知道你是否留意 struct类里面不能定义函数
例子1:
#include <stdio.h>
struct Student {
int a;
int b;
void show() {
//非法的
}
};
int main() {
return 0;
}
#include <iostream>
using namespace std;
struct Student {
int a;
int b;
void show() {
//合法的
}
};
int main() {
return 0;
}
通过上面的这个例子我们可以看出,
在看一下第二个例子
例子2
#include <stdio.h>
struct Student {
int a;
int b;
};
int main() {
struct Student s1;//合法的
Student s1;//非法的
return 0;
}
#include <iostream>
using namespace std;
struct Student {
int a;
int b;
void show() {
//合法的
}
};
int main() {
struct Student s1;//合法的
Student s1;//合法的
return 0;
}
通过例子2我们可以看出,在C中如果不用typedef关键字,我们定义结构体变量只能用struct Student,使用Student是不合法的,而在C++中这俩种方式都是合法的
结合这俩点我们不难看出 C++对结构体类型进行了某种程度的升级
“某种程度”–>函数的升级,以及定义“变量”的升级,都更加方便了
这就是我们今天的主题,在这里就引出了
可以把类看成就C中struct类型的plus版
结构体和类的唯一区别就是默认访问权限和继承权限
1.封装
2.继承
3.多态
!!C++中认为万事万物皆为对象,对象有它的属性和行为
属性–>成员变量
行为–>成员函数
不要忘了分号!!!
int a=10;
和int一个a等于10一样,必须加分号
C语言太自由了主函数中向修改结构体类型里面变量的值就修改
C++把它们封装了起来,加了访问权限的控制
1.public
2.protected
3.private
C++中类下默认访问权限是private
struct类的默认访问权限是public,因为要兼容C中的struct类
#include<iostream>
using namespace std;
struct Student {
//struct类里面没有说明什么权限 默认权限是public
//class类里面没有说明什么权限 默认权值是private
string _name;
string _num;
void Set_name(string name) {
_name = name;
}
void Set_num(string num) {
_num = num;
}
void show() {
cout << _name << endl << _num << endl;
}
};
int main() {
Student s1;//合法的
s1._name = "123";//合法的默认是public
return 0;
}
#include<iostream>
using namespace std;
class Student {
//struct类里面没有说明什么权限 默认权限是private
//class类里面没有说明什么权限 默认权值是class
string _name;
string _num;
void Set_name(string name) {
_name = name;
}
void Set_num(string num) {
_num = num;
}
void show() {
cout << _name << endl << _num << endl;
}
};
int main() {
Student s1;//合法的
s1._name = "123";//非法的 默认位private 访问不到
return 0;
}
public–>共有的作用域为 类内函数、子类和对象
protected–>受保护的作用域为 类内函数、子类
private–>私有的作用域 类内函数
先看个例子
#include<iostream>
using namespace std;
class Student {
//struct类里面没有说明什么权限 默认权限是private
//class类里面没有说明什么权限 默认权值是class
private:
string _name;
string _num;
public:
void Set_name(string name) {
_name = name;
}
void Set_num(string num) {
_num = num;
}
void show() {
cout << _name << endl << _num << endl;
}
};
int main() {
Student s1;//合法的
Student s2;
s1.Set_name("syc");//语句1
s2.Set_name("lh");//语句2
return 0;
}
语句1要执行的是把字符串syc赋值给s1的_name
语句2要执行的是把字符串lh赋值给s2的_name
this指针里面存的就是s1和s2的地址
#include<iostream>
using namespace std;
class Student {
private:
string _name;
string _num;
public:
void Set_name(Student* const this1,string name) {//指针常量 指向方向不变
this1->_name = name;
}
void Set_num(Student* const this2,string num) {
this2->_num = num;
}
void show() {
cout << _name << endl << _num << endl;
}
};
int main() {
Student s1;//合法的
Student s2;
s1.Set_name(&s1,"syc");
s2.Set_name(&s2,"lh");
return 0;
}
特点:其实this指针的特性在上一个模块(this指针的引出)已经讲解的差不多了,这里简要说明:
1.this指针的类型:指针常量指向调用这个函数的对象
2.只能在“成员函数”的内部使用
3.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所4.以对象中不存储this指针。
5.this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递此处为摘抄。
注意:计算类的大小不包括成员函数,只计算成员变量
构造函数是在创建对象的时候对成员变量进行赋值的一个函数
<注意>
构造函数是对成员变量进行赋值,而不是初始化
因为是先有了对象同时对象里面的成员变量被实例化了,之后对象调用构造函数,对成员变量进行赋值
观察这个格式我们可以看出它相对于普通函数的相同点和不同点
相同点:它与普通函数一样都有函数名、参数列表、函数体
不同点:它没有返回值 并且它强制要求函数名必须是类名
把类名作为函数名可以很明显的区分出来 这个是构造函数 方便你找到它
解释:如果在类里面没有自己定义构造函数的话,编译器会自动提供一个无参的默认构造函数
<注意>:如果定义了构造函数,那么编译器不会提供默认构造函数
按参数分:有参构造、无参构造
按类型分:普通构造、拷贝构造
//构造函数的分类
Pointer() {//无参构造
cout << "调用无参构造" << endl;
}
//explicit
Pointer(int* _p1) {//禁止隐式调用的有参构造
cout << "调用有参构造 再堆区申请空间" << endl;
}
Pointer(int a) {//有参构造
p1 = new int[a];
cout << "调用有参构造" << endl;
}
1.括号调用(常用)
2.显示调用
3.隐式调用
如何禁止隐式调用–>使用explicit关键字
int main() {
//构造函数调用时候的分类
int a=2;
int* pt = &a;
Pointer s1(pt); //括号调用
Pointer s2 = Pointer(pt);//显示调用
Pointer s3=pt; //隐式调用-->只能作用于需要传递1个参数的构造函数
//explicit 关键字 禁止隐式调用
Pointer* s4 = new Pointer(a);
delete s4;
return 0;
}
Pointer(int a) {//有参构造 p1 = new int[a]; cout << "调用有参构造" << endl; } Pointer* s4 = new Pointer(a);
这代码的内存分区图如下
#include <iostream>
#include <cmath>
using namespace std;
class Pointer {
public:
int* p1;
int p2;
//模拟的默认构造函数 当类内没有构造函数的时候 编译器会自己生成一个默认无参构造函数
Pointer() {
cout << "无参构造函数" << endl;
}
//有参构造函数
Pointer(int n) {
if (n) {
p1 = new int[n];
cout << "使用有参构造函数再堆区中开辟了" << n << "个int这么大小的空间 对象的成员变量p1指向这段空间" << endl;
}
else
cout << "输入的n不合法" << endl;
}
//构造函数的重载 第二个函数与第三个函数 函数名相同但是参数列表不同符合函数重载
Pointer(int a, int b) {
p2 = a + b;
cout << "构造函数有重载!" << endl;
}
//判断函数是否能够重载的时候要关注形参是否有默认参数
//Pointer(int a, int b, int c = 1) {
// cout << "";
//}//符合函数重载的条件,但是由于默认参数的出现,编译器懵逼了 都2个参数 不知道调用哪个了
};
int main() {
Pointer s1;//调用默认无参构造
Pointer s2();//看着也像是一个无参默认构造函数 但是编译器会把它识别成一个函数声明
Pointer s3(5);//调用有参构造
Pointer s4(1, 4);//构造函数也有重载 出现了默认参数
//Pointer s5(1, 3, 5);//构造函数也有重载
return 0;
}
构造函数也像普通函数一样具有重载,及函数名都是相同的类名,参数列表不同,符合函数重载
但是要注意的和普通函数一样,当出现默认参数的时候,要仔细观察它是否符合函数重载
看下面这段代码:
Pointer() {
cout << "无参构造函数" << endl;
}
Pointer s2();//错误 看着也像是一个无参默认构造函数 但是编译器会把它识别成一个函数声明
Pointer s2;//正确
看着是不是很想是一个无参构造函数,但是实际上编译器很认为他是一个函数的声明;
用于释放成员变量指向的堆区空间
当对象要被销毁的前 编译器自动调用析构函数
~类名(){ }
与构造函数比 多了一个~表示析构函数
析构函数不存在重载,它参数列表参数为空
~Pointer() {
cout << "调用了析构函数" << endl;
}
Pointer* s5= new Pointer();
delete s5;
delete先调用析构函数,释放掉成员变量所指向的堆区空间,然后调用free函数把对象所在的堆区空间释放
new返回的地址是自动转换的
malloc返回的地址是需要强转的
new是运算符,可以调用重载运算符函数(operator)进行重载
而malloc函数是C语言的库函数,C语言没有函数的重载,所以malloc没有函数重载
new是不需要传参的,它分配空间的大小由编译器根据类型计算得出
而malloc是需要传参的,传递的是具体开辟空间的大小的字节数
new是先调用malloc函数,先在堆区中开辟,如果这段空间的类型是类的话,会调用构造函数,对对象里面的成员变量进行赋值
而malloc仅仅只是在堆区中开辟空间
new开辟空间失败会抛出一段异常
而malloc会返回一个空指针
new不支持内存的扩张
malloc可以调用realloc扩张内存
delete关键字会先调用析构函数,再调用free函数
具体过程如下:delete函数会先调用析构函数释对象中成员变量所指向的堆区空间,然后再调用free函数释放对象所在的堆区空间
而free函数,不会调用析构函数,只释放对象所在的堆区空间
Pointer() {
cout << "无参构造函数" << endl;
}
Pointer s2();//错误 看着也像是一个无参默认构造函数 但是编译器会把它识别成一个函数声明
Pointer s2;//正确
看这段代码,它的内存分区图如下
new正常搭配的是delete–>free+析构
所以当成员变量没在堆区申请空间的时候,也就是析构函数不起作用的时候 delete可以替换free 但是别给自己找麻烦还是别这么用了!
初始化列表与构造函数紧密相关的
A(int a, int b, int c) :_b(b),_a(_b), _c(c) {//初始化列表
cout << _a << " " << _b << " " << _c << endl;
}
当父类没有无参构造时,需要通过父类名调用父类的构造函数
非类成员,通过成员名(值)初始化
类成员,通过对象调用构造函数
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class A{
int a1;
int a2;
public:
A(int _a1,int _a2){
a1=_a1;
a2=_a2;
}
};
class B:public A{
int b1;
A a3;
public:
B(int _a1,int _a2,int _b1,int _a31,int _a32):
//初始化列表-->1.给继承过来的父类的成员变量初始化,必须通过父类名显示调用
// 2.给本类下的成员变量进行初始化,类成员通过对象名括号调用构造函数
A(_a1,_a2),a3(_a31,_a32){
b1=_b1;
}
};
void text2_01(){
B b(1,2,3,4,5);
//调用无参构造函数不用加括号
}
因为对象的初始化只发生在对象创建的时候,而对象创建的时候会调用构造函数
1.初始化阶段:无论成员变量是否在初始化列表中,都会进行初始化
2.赋值阶段:根据函数体里面内容进行赋值
int _a;
int _b;
int _c;
A(int a, int b, int c) :_b(b),_a(_b), _c(c) {//初始化列表
cout << _a << " " << _b << " " << _c << endl;
}
A(1, 2, 3);
因为他们三必须创建的时候就进行初始化!!!
首先const类型的变量和引用类型都必须进行初始化操作,否则会报错
B中有A类成员,要先创建B中的成员变量,调用B的构造函数,然后再对B中的成员变量进行赋值
先调用类成员的构造函数,再调用本类的构造函数,最后先调用本类的析构函数,再调用类成员的析构函数
#include <iostream>
using namespace std;
class A {
int a;
int b;
int c;
public:
A(int _a, int _b, int _c) {
a = _a;
b = _b;
c = _c;
cout << "调用A的构造函数" << endl;
}
~A() {
cout << "调用A的析构函数" << endl;
}
};
class B {
int x1;
int x2;
const int cn;
int& m;
A s;
public:
B(int _x1,int _x2,int _cn, int _m, int _a, int _b, int _c) :cn(_cn), m(_m), s(_a, _b, _c) {
x1 = _x1;
x2 = _x2;
cout << "调用B的构造函数" << endl;
}
~B() {
cout << "调用B的析构函数" << endl;
}
};
int main() {
int x = 1;
B b(6,7,1, x, 2, 3, 4);
return 0;
}
不能调用构造函数初始化,因为构造函数归类所有,不是对象所有
//对象名.静态成员变量
//类名::静态成员变量
静态成员变量的初始化和全局变量的初始化
#include<iostream>
using namespace std;
class B {
public:
B() {
cout << "B的构造函数" << endl;
}
};
class A {
int a;
public:
static B m;//静态的类成员
static int n;
static void Show() {
cout << "hello" << endl;
}
};
int A::n = 0;//类外初始化
B A::m=B();
/*
* 1.静态成员变量属于类,不属于对象,多个对象共享一个静态成员变量
* 2.静态成员变量在类内定义,类外初始化
* 3.可以通过对象名或类名直接访问公有的静态成员变量
* 4.静态成员变量在编译阶段分配内存,也就是主函数之间
* 5.主函数之前会执行什么?
* 静态成员变量的初始化和全局变量的初始化
*/
int main() {
cout << "__________________________" << endl;
A a1,a2,a3;
a1.n++;
a2.n++;
a3.n++;
A::n++;
cout << A::n << endl;
cout << a1.n << endl;
cout << a2.n << endl;
cout << a3.n << endl;
return 0;
}
因为没有this指针的话,编译器就无法确定究竟是对哪个对象的非静态成员变量进行操作
#include <iostream>
using namespace std;
class A {
int b;
static int a;
public:
static void func1() {
}
static void func() {
this->b = 1;
func1();
a = 2;
}
};
int A::a = 0;
int main() {
return 0;
}
#include <iostream>
using namespace std;
class A {
int a = 2;
public:
void func1() {
a = 1;
cout << "1" << endl;
}
static void func2() {
cout << "2" << endl;
}
};
int main() {
A* a = nullptr;
return 0;
a->func1();
a->func2();
A::func2();
}
if(this==NULL) return ;
—————————————————————————————————————————————————————
因为有类型的内存叫类,所以要是类里面啥也没有就给它一个1字节大小
1.因为有const,所以常对象里面的成员变量的值不能修改
2.常对象只能调用常函数
3.非常对象优先调用非常函数,若无非常函数,再调用常函数
void get_nums() const {
this->a = 2;
}
如果有俩个函数,它的函数名和参数均相同,什么情况下会发生函数重载?
在这俩个函数一个是常函数,一个是非常函数的时候会发生函数重载,因为编译器会根据对象是否是常对象,来调用不同的函数。
#include <iostream>
using namespace std;
class Person {
mutable int a;
char b;
public:
Person() {
cout << "构造函数" << endl;
}
~Person() {
cout << "析构函数" << endl;
}
void get_nums() {
this->a = 2;
}
void get_nums() const {
this->a = 2;
}
void get_nums2() {//非常函数
//this指针是一个指针常量-->Type* const this(指向不能改变,但是可以修改里面的内容)
a = 2;
}
void get_nums2cst() const {//常函数
//this指针是一个常量指针常量-->const Type* const this (指针的指向和指向对象里面的内容都不可以改变)
a = 2;//常函数,不能修改成员变量的值
}
};
void show() const {//常函数只能再类中定义
//错误!
}
int main() {
Person p1;//非常对象
const Person p2;//常对象-->对象里面的成员变量的值不可以修改
p1.get_nums2();//非常对象调用非常函数
p1.get_nums2cst();//非常对象调用常函数
p2.get_nums2cst();//常对象调用常函数
p2.get_nums2();//常对象不可以调用非常函数
/*
* 总结:常对象只能调用常函数,非常对象优先调用非常函数,没有非常函数再调用常函数
*/
p1.get_nums();
p2.get_nums();//函数的重载,编译器可以通过调用对象的不同来区分调用常函数还是非常函数
return 0;
告诉我想访问的那个类,我是你们这个类的我的好朋友,可以让我访问私有成员属性
#include <iostream>
using namespace std;
class Building {
friend void GoodFreind(Building& b);
string bedroom;//私有的
public:
string sittingroom;
Building();//类内声明构造函数
/*{
this->bedroom = "卧室";
this->sittingroom = "客厅";
}*/
};
Building::Building() {
this->bedroom = "卧室";
this->sittingroom = "客厅";
}
void GoodFreind(Building& b) {//当全局函数想访问一个类中的私有成员属性的时候,把这个函数声明成友元函数就可以实现了
cout << "朋友相进入" << b.sittingroom << endl;
cout << "朋友想进入" << b.bedroom << endl;
}
int main() {
Building b;
GoodFreind(b);
return 0;
}
友元除了前面讲过的函数以外,友元还可以是类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数。
当一个函数是另外一个类的友元函数的时候,那么它就可以访问另一个类的私有成员变量
#include <iostream>
using namespace std;
class Building {
friend Friend;//Friend类设为友元
string bedroom;//私有的
public:
string sittingroom;
Building();//类内声明构造函数
};
class Friend {
Building *b;
public:
Friend();//类内声明构造函数
~Friend();
void vist();7
};
Building::Building() {
this->bedroom = "卧室";
this->sittingroom = "客厅";
}
Friend::Friend(){
b = new Building();//成员变量指向堆区空间了,要调用析构函数
}
Friend::~Friend() {
if(b) delete b;
}
void Friend::vist() {//一个类想访问另一个类的私有成员,把这个类变为友元
cout << "朋友想进入" << b->sittingroom;
cout << "朋友想进入" << b->bedroom;
}
int main() {
Building b;
return 0;
}
#include <iostream>
using namespace std;
class Friend {
Building* b;
public:
Friend();//类内声明构造函数
~Friend();
void vist();
};
class Building {
friend void Friend::vist();//Friend类里面的成员函数设为友元
string bedroom;//私有的
public:
string sittingroom;
Building();//类内声明构造函数
};
class A{
public:
void visit();
};
Building::Building() {
this->bedroom = "卧室";
this->sittingroom = "客厅";
}
Friend::Friend(){
b = new Building();//成员变量指向堆区空间了,要调用析构函数
}
Friend::~Friend() {
if(b) delete b;
}
void Friend::vist() {//一个类想访问另一个类的私有成员,把这个类变为友元
cout << "朋友想进入" << b->sittingroom;
cout << "朋友想进入" << b->bedroom;
}
int main() {
Building b;
return 0;
}
好处:可以访问类中的私有成员
坏处:破坏了封装性
1.对象调用get set函数
2.把一个类和全局函数设置为友元
因为C++提供的加法运算符只能满足基本数据类型间的加法,如果我想让俩个相同的类的对象进行加法的话会报错
所以为了能让俩个相同类的对象进行加法,我们要把这个过程封装到一个函数里面,只不过要多加一个关键字operator而已,让编译器一下子就找到,这个是重载运算符的函数
作用:实现俩个自定义运算符相加
可以少传递一个参数
#include <iostream>
using namespace std;
class Box {
int length;
int width;
int height;
public:
Box() {
length = 0;
width = 0;
height = 0;
}
Box(int length,int width,int height) {
this->length = length;
this->width = width;
this->height = height;
}
Box(const Box& other) {
length = other.length;
width = other.width;
height = other.height;
}
//成员函数重载加法运算符
Box operator+(const Box& other) {
Box p;
p.length = this->length + other.length;
p.width = this->width + other.width;
p.height = this->height + other.height;
return p;
}
};
int main() {
Box a(1,2,3);
Box b(2, 3, 4);
Box c = a + b;//直接调用
Box d;
d = a.operator+(b);//调用重载运算符的函数
return 0;
}
#include <iostream>
using namespace std;
class Box {
int length;
int width;
int height;
friend Box operator+(const Box& other1, const Box& other2);
friend Box operator+(const Box& other1, int val);
public:
Box() {
length = 0;
width = 0;
height = 0;
}
Box(int length,int width,int height) {
this->length = length;
this->width = width;
this->height = height;
}
Box(const Box& other) {
length = other.length;
width = other.width;
height = other.height;
}
};
//全局函数重载加法运算符
Box operator+(const Box& other1,const Box& other2) {//不调用成员函数是无法访问私有的成员变量的,需要设置为友元,告诉编译器,我这个重载运算符的函数是你这个类的好朋友,都哥们,能f
Box p;
p.length = other1.length + other2.length;
p.width = other1.width + other2.width;
p.height = other1.height + other2.height;
return p;
}
Box operator+(const Box& other1,int val) {
Box p;
p.length = other1.length + val;
p.width = other1.width + val;
p.height = other1.height + val;
return p;
}
int main() {
Box a(1,2,3);
Box b(2, 3, 4);
Box c = a + b;
Box d;
d=operator+(a,b);
return 0;
}
#include <iostream>
using namespace std;
class Box {
int length;
int width;
int high;
friend Box& operator+=(Box& other1, Box& other2);
public:
Box() {
length = 1;
width = 2;
high = 3;
}
/*Box& operator+=(const Box& other) {
this->length += other.length;
this->width += other.width;
this->high += other.high;
return *this;
}*/
int get_length() {
return this->length;
}
};
Box& operator+=(Box& other1,const Box& other2) {
other1.length += other2.length;
other1.width += other2.width;
other1.high += other2.high;
return other1;
}
int main() {
Box a, b,c;
a += b+=c;//隐式调用函数
cout << a.get_length();
return 0;
}
cout是ostream类的对象
cin是istream类的对象
#include <iostream>
using namespace std;
class Box {
int length;
int width;
int high;
friend ostream& operator<<(ostream& o, const Box& b);
public:
Box() {
length = 1;
width = 2;
high = 3;
}
};
ostream& operator<<(ostream& o,const Box& b) {
o << b.length << ' ' << b.width << ' ' << b.high << endl;
return o;
}
int main() {
Box a,b,c;
//cout << a;没有与这些操作数相匹配的运算符
/*
* 你想重载一个运算符要么在类内重载,要么在类外重载
* 但是cout对象属于ostream类,该类我们无法修改,所以只能在类外用全局函数重载
*/
cout << a << b << c;
return 0;
}
#include <iostream>
using namespace std;
class Box {
int length;
int width;
int high;
friend ostream& operator<<(ostream& o, const Box& b);
friend istream& operator>>(istream& i, Box& b);
public:
Box() {
length = 1;
width = 2;
high = 3;
}
};
ostream& operator<<(ostream& o,const Box& b) {
o << b.length << ' ' << b.width << ' ' << b.high << endl;
return o;
}
istream& operator>>(istream& i,Box& b) {
i >> b.length;
i >> b.width;
i >> b.high;
return i;
}
int main() {
Box a, b,c;
//cout << a;没有与这些操作数相匹配的运算符
/*
* 你想重载一个运算符要么在类内重载,要么在类外重载
* 但是cout对象属于ostream类,该类我们无法修改,所以只能在类外用全局函数重载
*/
cin >> a>>b>>c;
cout << a << b << c;
return 0;
}
#include <iostream>
using namespace std;
class A {
//private 类内
int a;
int b;
int c;
friend ostream& operator<<(ostream& o, const A& other);
public:
A() {
this->a = 0;
this->b = 0;
this->c = 0;
}
A& operator++() {//前加加
a++;
return *this;
}
A operator++(int) {//后加加
//不要返回局部变量的引用
A t = *this;
a++;
return t;
}
};
//
ostream& operator<<(ostream& o, const A& other) {
o << other.a << endl;
return o;
}
int main() {
A s;
cout << s++;
cout << ++s;
return 0;
}
#include <iostream>
#include <iostream>
using namespace std;
class Person {
string name;
int age;
public:
Person(string name, int age) {
this->name = name;
this->age = age;
}
bool operator==(const Person& other) const{
if (this->age == other.age) {
return 1;
}
return 0;
}
bool operator>(const Person& other) const{
if (this->age > other.age) {
return 1;
}
return 0;
}
bool operator<(const Person& other) const{
if (this->age < other.age) {
return 1;
}
return 0;
}
bool operator!=(const Person& other) const{
if (this->age != other.age) {
return 1;
}
return 0;
}
};
int main() {
Person p0("施易辰", 20);
Person p1("田雪嵩", 100);
if (p0 == p1) {
cout << "年龄相等" << endl;
}
else if (p0 > p1) {
cout << "p0大于p1" << endl;
}
else if (p0 < p1) {
cout << "p0小于p1" << endl;
}
else
cout << "p0不等于p1" << endl;
return 0;
}
#include <iostream>
#include <iostream>
using namespace std;
class Person {
string name;
int age;
int* password;
friend ostream& operator<<(ostream& o, const Person& p);
public:
Person(string name, int age, int mm) {
this->name = name;
this->age = age;
this->password = new int[10];
*password = mm;
}
~Person() {
if (password) delete[]password;
}
};
ostream& operator<<(ostream& o, const Person& p) {
o << p.name << ' ' << p.age << *(p.password) << endl;
return o;
}
int main() {
Person p0("施易辰", 20, 12345);
Person p1("田雪嵩", 100, 123425);
p0 = p1;//编译器自动提供的是浅拷贝类型的赋值运算符 成员变量指向堆区空间时候会报错
cout << p0 << p1;
return 0;
}
#include <iostream>
#include <iostream>
using namespace std;
class Person {
string name;
int age;
int* password;
friend ostream& operator<<(ostream& o, const Person& p);
public:
Person(string name, int age, int mm) {
this->name = name;
this->age = age;
this->password = new int[10];
*password = mm;
}
~Person() {
if (password) delete[]password;
}
Person& operator=(const Person& other) {//深拷贝模式的赋值运算符
this->name = other.name;
this->age = other.age;
this->password = new int[10];
*password = *(other.password);
return *this;
}
};
ostream& operator<<(ostream& o, const Person& p) {
o << p.name << ' ' << p.age << *(p.password) << endl;
return o;
}
int main() {
Person p0("施易辰", 20, 12345);
Person p1("田雪嵩", 100, 123425);
p0 = p1;//调用自定义的赋值运算符的重载函数
cout << p0 << p1;
return 0;
}
不会报错!
#include <iostream>
using namespace std;
class Myprint {
public:
void operator()(string text) {
cout << text << endl;
}
};
class Myadd {
public:
int operator()(int a, int b) {
return a + b;
}
};
void text01() {
Myprint myprint;
myprint("abc");
myprint.operator()("dce");
}
void text02() {
int a = 1;
int b = 2;
Myadd myadd;
cout << myadd.operator()(1, 2) << endl;
cout << myadd(3, 4) << endl;
}
int main() {
text01();
text02();
return 0;
}
class A{
int a;
int b;
int c;
};
class B:public A{
//在子类中,abc已经被继承过来了,不用写重复的代码
};
A被称为父类、基类
B被称为子类、派生类
和访问权限一样,同样有三种
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class father{
private:
int pri;
void prifun();
protected:
int pro;
void profun();
public:
int pub;
void pubfun();
};
class son:private father{//继承权限作用:缩小父类成员在子类中的访问权限,只会缩小不会扩大
/*
* 私有的继承权限,缩小了子类的访问权限
* 成员变量pro和成员函数profun都变成私有的了,在grandson中可以看出
* 成员变量pub和成员函数pubfun都变成私有的了,
*/
void fun(){
pri=1;//错误
prifun();//错误,父类中私有的成员子类都不可以访问
pro=1;//父类中受保护的和共有的成员子类可以访问
profun();
pub=1;
pubfun();
}
};
class grandson:public son{
void fun(){
pro=1;//错误
profun();//错误
pub=1;//错误
pubfun();//错误
}
};
void text01(){
grandson s;
s.pub=1;//错误
}
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class father{
private:
int pri;
void prifun();
protected:
int pro;
void profun();
public:
int pub;
void pubfun();
};
class son:protected father{//继承权限作用:缩小父类成员在子类中的访问权限,只会缩小不会扩大
void fun(){
pri=1;//错误
prifun();//错误,父类中私有的成员子类都不可以访问
pro=1;//父类中受保护的和共有的成员子类可以访问
profun();
pub=1;
pubfun();
}
};
class grandson:public son{
void fun(){
pro=1;
profun();
pub=1;
pubfun();
}
};
void text01(){
grandson s;
s.pub=1;//错误
}
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class father{
private:
int pri;
void prifun();
protected:
int pro;
void profun();
public:
int pub;
void pubfun();
};
class son:public father{//继承权限作用:缩小父类成员在子类中的访问权限,只会缩小不会扩大
/*
* 私有的继承权限,缩小了子类的访问权限
* 成员变量pro和成员函数profun都变成私有的了,在grandson中可以看出
* 成员变量pub和成员函数pubfun都变成私有的了,
*/
void fun(){
pri=1;//错误
prifun();//错误,父类中私有的成员子类都不可以访问
pro=1;//父类中受保护的和共有的成员子类可以访问
profun();
pub=1;
pubfun();
}
};
class grandson:public son{
void fun(){
pro=1;
profun();
pub=1;
pubfun();
}
};
void text01(){
grandson s;
s.pub=1;//可以
}
父类中如果有private类型的成员,无论以何种继承方式,都无法在子类中访问
虽然是无法访问,但是它仍旧被继承了过来,只是被隐藏了,仍然占用子类的空间
当创建子类对象的时候,会优先调用父类的构造函数,然后调用子类的构造函数
为什么?
因为子类对象需要先继承父类的成员变量,也就是先创建父类的成员变量,然后要对父类对象赋值,所以调用父类构造函数,然后再创建自己的成员变量,调用自己的构造函数
入栈出栈顺序有关
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class father{
public:
father(){
cout<<"father构造"<<endl;
}
~father(){
cout<<"father析构"<<endl;
}
};
class son:public father{
public:
son(){
cout<<"son构造"<<endl;
}
~son(){
cout<<"son析构"<<endl;
}
};
class grandson:public son{
public:
grandson(){
cout<<"grandson构造"<<endl;
}
~grandson(){
cout<<"grandson析构"<<endl;
}
};
void text01(){
grandson gr;
}
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class father{
public:
father(){
cout<<"father构造"<<endl;
}
void fun(){
cout<<"father的fun函数"<<endl;
}
};
class son:public father{
public:
son(){
cout<<"son构造"<<endl;
}
void fun(){
cout<<"son的fun函数"<<endl;
}
};
class grandson:public son{
public:
grandson(){
cout<<"grandson构造"<<endl;
}
void fun(){
cout<<"grandson的fun函数"<<endl;
}
};
void text01(){
father f;
son s;
grandson gr;
f.father::fun();
s.son::fun();
gr.grandson::fun();
}
静态成员变量俩种调用方式,作用域和对象名调用
但是无论是哪种调用,再同名静态成员变量继承中,都要加上作用域
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class father{
public:
static int a;
};
int father::a=1;
class son:public father{
public:
static int a;
};
int son::a=2;
class grandson:public son{
public:
static int a;
};
int grandson::a=3;
void text01(){
grandson gr;
cout<<gr.father::a<<endl;//对象调用
cout<<gr.son::a<<endl;
cout<<gr.a<<endl;
cout<<father::a<<endl;//作用域调用
cout<<son::a<<endl;
cout<<grandson::a<<endl;
}
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class A{
public:
int a;
A(){
cout<<"A构造"<<endl;
}
};
class B{
public:
int a;
B(){
cout<<"B构造"<<endl;
}
};
class C:public B,public A{
public:
C(){
cout<<"C构造"<<endl;
}
};
void text01(){
C c;
c.B::a;//多继承问题中会存在二义性-->解决方式,作用域
c.A::a;
}
同样存在二义性但是与多击沉不同可以通过virtual来解决,也可以通过作用域解决
#pragma once
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
class A{
public:
int a;
A(){
cout<<"A构造"<<endl;
}
};
class B:virtual public A{
public:
B(){
cout<<"B构造"<<endl;
}
};
class C:virtual public A{
public:
C(){
cout<<"C构造"<<endl;
}
};
class D:public B,public C{
public:
D(){
cout<<"D构造"<<endl;
}
};
void text01(){
D d;
d.B::a;
d.a;
}