C++笔记

1.输入输出流

在C中要想输入和输出 我们会经常用到

#include <stdio.h>

在C++中头文件的命名风格不用.h

#include <iostream>using namespace std;

为什么要用上面俩句话的解释(自己写的博客)

c++中 为什么要写<iostream>和using namespace std;-CSDN博客

cin cout

这俩个函数是在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;
}

2.bool类型

c语言没有 是C++中的一种基本数据类型(内置数据类型)

只用一个字节

0为false

1为true

非零的数组都是true

3.对象的含义

一块有类型的内存

4.用new关键字申请堆区空间(delete)

我们在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;
}

1.什么是引用

在C++语法上来看,引用是给变量起了个别名,不占用空间

2.引用的格式

类型& 引用变量名 =引用实体

3.引用的注意事项

a.引用定义的同时必须进行初始化,且不能初始化为空

引用是给变量起别名,那前提肯定得有实体变量

引用是给变量起别名,给空起了个别名没意义

c.不可以改变引用关系

这与引用的实质有关系

引用的实质是一个指针常量,是一个指向方向固定的指针,所有它不能更改指向,也就不能改变引用关系

int& b=a;
int* const b=&a;
image-20231127111656885
#include <iostream>using namespace std;int main() {int a = 5;int& b3;//引用必须初始化int& b2 = NULL;//引用不能初始化为空int& b1 = a;int* const b1 = &a;//本质 -->指针常量(指向不能改变,但是指向内存空间里面的内容可以改变)ncout << b1 << " " << *b2;return 0;
}

4.C++中的左值和右值

左值

可寻址、有变量名的值

右值

不可寻址、一般没有变量名的值

左值引用

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;
}

5.引用的应用

做函数的形参

参数的传递方式有俩种

值传递(只是一个传值的过程),但是传递的值可能是地址,也可能是基本类型数据

引用传递

#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;
}

优点

指针要判空的,但是引用不用 可以避免很多麻烦

引用做函数的返回值

在内存中不会产生返回值的副本

6.引用与指针的区别

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;
}
image-20231127113815831

5.没有多级引用,但是有多级指针,二级三级指针

6.引用的++是指别名所在的那段空间数据的++,而指针的++是指向的移动

7.引用作为函数返回值的好处

可以避免调用拷贝构造函数,节约时间的成本

1.函数默认参数

函数栈

函数调用,是主调函数向被调函数传值,然后被调函数返回结果给主调函数的一个过程

这个过程是需要函数栈来辅助的

栈是向下生长的,就是由高地址向低地址开辟空间

堆是向上生长的,就是由低地址向高地址开辟空间

形参在入栈的时候是从右向左入栈的,而实参传值给形参的时候是从左向右传值(相当于出栈的顺序)

函数的默认参数是在函数调用的过程中发生的,实参在没有传递给形参值之前,形参就已经有了默认参数

格式:返回值类型 函数的名字(形参=默认值){ }

#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 10func1(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;
}

2.函数的重载

为什么C++有重载但是C语言却没有呐?

原因:C语言函数的组成与C++函数组成有区别

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;
}

编译器无法仅根据返回值的类型来判断是否能够重载

函数的重载是一个静态的多态,它先编译阶段就能够判断运行的是哪个函数

3.递归

递归的模板(4件套)

1.结束条件

2.前进

3.递归的调用

4.回退

int func(){if();//递归的结束条件//前进func();//递归的调用//回退
}

斐波那契数列的递归

为什么斐波那契数列能用递归

能写出递归树

比如你想算第五项的斐波那契数列

你要想求5 就必须得知道4和3 你要想知道4和3 就必须得知道2和1 你要想知道 2和 1 就必须知道1 和 0 而1 和0 我们还真知道 所以符合递归

递归树如图:

image-20231130121659310
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类与C++中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;
}

通过上面的这个例子我们可以看出,

struct类型在C中不可以定义函数,但是在C++中可以定义函数

在看一下第二个例子

例子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++中结构体名称可以定义变量

结合这俩点我们不难看出 C++对结构体类型进行了某种程度的升级

“某种程度”–>函数的升级,以及定义“变量”的升级,都更加方便了

这种升级就是把struct升级成了C++语言中的类

这就是我们今天的主题,在这里就引出了

2.类

可以把类看成就C中struct类型的plus版

结构体和类的唯一区别就是默认访问权限和继承权限

C++面向对象的三大特点

1.封装

2.继承

3.多态

!!C++中认为万事万物皆为对象,对象有它的属性和行为

属性–>成员变量

行为–>成员函数

封装

格式:class 类名 { 访问权限:属性、行为};

不要忘了分号!!!

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类里面没有说明什么权限 默认权值是privatestring _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";//合法的默认是publicreturn 0;
}
#include<iostream>
using namespace std;
class Student {//struct类里面没有说明什么权限 默认权限是private//class类里面没有说明什么权限 默认权值是classstring _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–>私有的作用域 类内函数

this关键字

先看个例子

#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");//语句1s2.Set_name("lh");//语句2return 0;
}

语句1要执行的是把字符串syc赋值给s1的_name

语句2要执行的是把字符串lh赋值给s2的_name

this指针里面存的就是s1和s2的地址

this关键字是指向当前对象的指针,谁调用这个函数,this指针就指向谁
本质:this指针的本质是在实参传值给形参的过程中,形参多了一个指针常量this用来接收调用这个函数对象的地址
#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寄存器自动传递,不需要用户传递

此处为摘抄。

类的对齐和结构体的对齐很相似

注意:计算类的大小不包括成员函数,只计算成员变量

1.构造函数

什么是构造函数?

构造函数是在创建对象的时候对成员变量进行赋值的一个函数

<注意>

构造函数是对成员变量进行赋值,而不是初始化

因为是先有了对象同时对象里面的成员变量被实例化了,之后对象调用构造函数,对成员变量进行赋值

构造函数的格式

类名+(){ }

观察这个格式我们可以看出它相对于普通函数的相同点和不同点

相同点:它与普通函数一样都有函数名、参数列表、函数体

不同点:它没有返回值 并且它强制要求函数名必须是类名

把类名作为函数名可以很明显的区分出来 这个是构造函数 方便你找到它

无参默认构造函数

解释:如果在类里面没有自己定义构造函数的话,编译器会自动提供一个无参的默认构造函数

<注意>:如果定义了构造函数,那么编译器不会提供默认构造函数

构造函数的分类及调用

分类

按参数分:有参构造、无参构造

按类型分:普通构造、拷贝构造

//构造函数的分类
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);

这代码的内存分区图如下

image-20231204124533552

构造函数的具体实现代码

#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;}elsecout << "输入的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;//正确

看着是不是很想是一个无参构造函数,但是实际上编译器很认为他是一个函数的声明;

在调用默认构造函数的时候不用加括号!!!!否则会被编译器认为是函数的声明!!!!!!!

2.析构函数

什么是析构函数?

用于释放成员变量指向的堆区空间

什么时候调用析构函数?

当对象要被销毁的前 编译器自动调用析构函数

析构函数的格式

~类名(){ }

与构造函数比 多了一个~表示析构函数

析构函数不存在重载,它参数列表参数为空

~Pointer() {cout << "调用了析构函数" << endl;
}
Pointer* s5= new Pointer();
delete s5;

delete先调用析构函数,释放掉成员变量所指向的堆区空间,然后调用free函数把对象所在的堆区空间释放

3.new与malloc的区别

从返回值上来看

new返回的地址是自动转换的

malloc返回的地址是需要强转的

从名称上

new是运算符,可以调用重载运算符函数(operator)进行重载

而malloc函数是C语言的库函数,C语言没有函数的重载,所以malloc没有函数重载

参数上来看

new是不需要传参的,它分配空间的大小由编译器根据类型计算得出

而malloc是需要传参的,传递的是具体开辟空间的大小的字节数

从底层在来看

new是先调用malloc函数,先在堆区中开辟,如果这段空间的类型是类的话,会调用构造函数,对对象里面的成员变量进行赋值

而malloc仅仅只是在堆区中开辟空间

从空间开辟失败的后果上来看

new开辟空间失败会抛出一段异常

而malloc会返回一个空指针

从已分配内存不够用扩张上来看

new不支持内存的扩张

malloc可以调用realloc扩张内存

4.delete与free的区别

1.从参数上来看–>delete不需要传递参数,free需要传参

2.从底层上来看

delete关键字会先调用析构函数,再调用free函数

具体过程如下:delete函数会先调用析构函数释对象中成员变量所指向的堆区空间,然后再调用free函数释放对象所在的堆区空间

而free函数,不会调用析构函数,只释放对象所在的堆区空间

问题1:为什么delete的底层不是先调用free函数再调用析构函数那??????

Pointer() {cout << "无参构造函数" << endl;}
Pointer s2();//错误 看着也像是一个无参默认构造函数 但是编译器会把它识别成一个函数声明
Pointer s2;//正确

看这段代码,它的内存分区图如下

image-20231204124533552

如果我们先调用的是free函数,那么对象所在的堆区空间会先被释放,那么成员变量如果有指向其他堆区空间的话,这段空间将会再也无法被找到了,会造成内存的泄露

但是如果先调用析构函数,先把成员变量指向的堆区空间释放了,就不会造成这个问题

!!!!故delete函数是先调用析构,再调用free

问题2:new可以和free一起搭配吗?

new正常搭配的是delete–>free+析构

所以当成员变量没在堆区申请空间的时候,也就是析构函数不起作用的时候 delete可以替换free 但是别给自己找麻烦还是别这么用了!

0.初始化列表是在构造函数中使用的

1.初始化列表的格式

初始化列表与构造函数紧密相关的

构造函数参数列表的后面加上:成员变量1(参数列表的值),成员变量2(参数列表的值)…

A(int a, int b, int c) :_b(b),_a(_b), _c(c) {//初始化列表cout << _a << " " << _b << " " << _c << endl;
}

2.初始化列表的作用?

1.给父类继承过来的成员变量进行初始化

当父类没有无参构造时,需要通过父类名调用父类的构造函数

2.给本类的成员变量进行初始化

非类成员,通过成员名(值)初始化

类成员,通过对象调用构造函数

#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);//调用无参构造函数不用加括号
} 

3.为什么要使用初始化列表?

原因1:const类型的成员变量和引用类型的成员变量必须初始化,而构造函数是在创建对象的时候对成员变量进行赋值,不是初始化,这样在编译的时候就会报错

原因2:类成员,比如说在类A里面定义类B的成员,如果类B有构造函数的话,系统将不会提供默认无参构造函数,那么如果不用初始化列表的话就会报错。

原因3:使用初始化列表会比正常赋值高效一些

4.始化列表特点

a.只能在构造函数中使用

因为对象的初始化只发生在对象创建的时候,而对象创建的时候会调用构造函数

构造函数的执行又俩个阶段

1.初始化阶段:无论成员变量是否在初始化列表中,都会进行初始化

2.赋值阶段:根据函数体里面内容进行赋值

b.成员变量初始化的顺序只于它在类中声明的顺序有关,与初始化列表的顺序无关

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);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

c.const常量、引用和类成员必须在初始化列表中初始化

因为他们三必须创建的时候就进行初始化!!!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

首先const类型的变量和引用类型都必须进行初始化操作,否则会报错

d.类对象做类成员

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;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.静态成员变量

格式:类内->在成员变量的类型前面加一个static

静态成员变量只能初始化一次

它存储在静态区,静态成员变量不占用对象的空间,在编译阶段分配内存,也就是主函数之前

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

静态成员变量属于类不属于对象,多个对象共享一个共有的静态成员变量

必须在类内定义,类外初始化

不能调用构造函数初始化,因为构造函数归类所有,不是对象所有

可以通过对象名或者类名直接访问静态成员变量

//对象名.静态成员变量
//类名::静态成员变量

静态成员变量在子类和父类共享静态成员变量

在主函数之前执行的有什么?

静态成员变量的初始化和全局变量的初始化

#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;
}

2.静态成员函数

成员函数存储在代码段内,不占用对象空间

静态成员函数属于类,不属于对象

可以通过类名和对象名去调用公有的静态成员函数

静态成员函数没有this指针,只能访问静态成员

因为没有this指针的话,编译器就无法确定究竟是对哪个对象的非静态成员变量进行操作

image-20231215191233091

成员函数不占用对象的空间,成员函数是统一放在代码段当中的,当对象想访问它的成员函数的时候,会到代码段中去寻找那段代码。成员函数通过this指针去区分究竟对哪个对象里面的数据进行操作

image-20231215190423890
#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;
}

3.在C++中空指针是可以访问成员函数的

#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();
}

但是要注意有没有用到this指针,因为空指针调用成员函数的时候this指针也为空,对象里面是没有内容的。

为了保持程序的健壮性,可以对成员函数进行判空操作

if(this==NULL) return ;

4.静态成员函数与常函数中this指针的区别

静态成员函数:没有this指针,无法访问对象,所以也不能访问对象里面的非静态成员变量

常函数:有this指针,但this指针的类型是一个常量指针常量,无法修改对象里面成员变量的值

5.为什么要有this指针?

—————————————————————————————————————————————————————

6.空类的大小为1字节

因为有类型的内存叫类,所以要是类里面啥也没有就给它一个1字节大小

1.常对象

格式:const Type a;//常对象

性质:

1.因为有const,所以常对象里面的成员变量的值不能修改

2.常对象只能调用常函数

3.非常对象优先调用非常函数,若无非常函数,再调用常函数

2.常函数

格式:在成员函数参数列表的后面加上一个const

void get_nums() const {this->a = 2;
}

性质:

1.常函数有this指针,但是类型是常指常类型,无法修改对象里面的成员变量的值,也不能调用非常函数

2.常函数与非常函数会发生函数重载

如果有俩个函数,它的函数名和参数均相同,什么情况下会发生函数重载?

在这俩个函数一个是常函数,一个是非常函数的时候会发生函数重载,因为编译器会根据对象是否是常对象,来调用不同的函数。

3.成员变量在声明的时候前面加mutable,就可以在常函数中修改其值

4.常函数只能再类中定义,出了类定义是错误的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.常函数不能调用非常函数,因为常函数的this指针为常指常,而非常函数的this指针为指常,类型不兼容

#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;

<注意>:如果函数名一样,参数列表也一样,也有可能发生函数重载,因为有常函数和非常函数,编译器通过常对象和非常对象来区分

3.常函数、非常函数与静态成员函数中this指针的区别

在非常函数中:this指针是一个指针常量,指向不可以改变

在常函数中:this指针是一个常量指针常量,指向和指向里面的内容均不可以改变,这也就是为什么常函数里面不可以修改成员的是属性。

静态成员函数中:根本没有this指针,所以无法修改对象里面成员变量的值

1.为什么要有友元?

一个类外函数(全局函数)或者类想访问另一个类私有的成员属性的时候,就要用到友元

关键词:friend

2.使用友元的三种情况

1.全局函数做友元

告诉我想访问的那个类,我是你们这个类的我的好朋友,可以让我访问私有成员属性

#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;
}

2.类做友元

友元除了前面讲过的函数以外,友元还可以是类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数。

当一个函数是另外一个类的友元函数的时候,那么它就可以访问另一个类的私有成员变量

#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;
}

3.类中的成员函数做友元

#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;
}

3.友元的好坏

好处:可以访问类中的私有成员

坏处:破坏了封装性

访问一个类的私有成员变量由俩种方法

1.对象调用get set函数

2.把一个类和全局函数设置为友元

运算符的重载有俩种形式,一种类内(成员函数)重载运算符,一种类外(全局函数)重载运算符,类内可以少传递一个参数,但是不是所有运算符重载都可以再类内中进行的,比如<< >>

1.重载’+'运算符

值返回

为什么要重载加法运算符?

因为C++提供的加法运算符只能满足基本数据类型间的加法,如果我想让俩个相同的类的对象进行加法的话会报错

image-20231218154406227

所以为了能让俩个相同类的对象进行加法,我们要把这个过程封装到一个函数里面,只不过要多加一个关键字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) {//不调用成员函数是无法访问私有的成员变量的,需要设置为友元,告诉编译器,我这个重载运算符的函数是你这个类的好朋友,都哥们,能fBox 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;
}

2.重载’+=’ 运算符

引用返回

#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;
}

3.重载’<<'运算符

引用返回

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;
}

4.重载’>>'运算符

引用返回

#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;
}

5.重载’++'运算符

前加加–>引用返回

后加加–>值返回

#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;
}

6.重载’比较’运算符

#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;}elsecout << "p0不等于p1" << endl;return 0;
}

7.重载’='赋值运算符

引用返回

编译器自动提供的赋值运算符是浅拷贝类型,如果成员变量指向堆区空间的话,会连续俩次析构函数会报错

#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;
}
image-20231224131403075

所以如果成员变量指向堆区空间的话要自己重载赋值运算符,类型为深拷贝模式

#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;
}
image-20231224131600567

不会报错!

8.重载’()'运算符

又称仿函数

#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;
}

1.继承的基本语法

class 类名:继承权限 父类{

}

class A{int a;int b;int c;
};
class B:public A{//在子类中,abc已经被继承过来了,不用写重复的代码
};

A被称为父类、基类

B被称为子类、派生类

继承的作用:可以减少重复的代码

2.继承权限

和访问权限一样,同样有三种

第一种private继承权限

#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;//错误
}

第二种protect继承权限

#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;//错误
}

第三种public继承权限

#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类型的成员,无论以何种继承方式,都无法在子类中访问

虽然是无法访问,但是它仍旧被继承了过来,只是被隐藏了,仍然占用子类的空间

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.继承中的构造析构顺序

构造函数

当创建子类对象的时候,会优先调用父类的构造函数,然后调用子类的构造函数

为什么?

因为子类对象需要先继承父类的成员变量,也就是先创建父类的成员变量,然后要对父类对象赋值,所以调用父类构造函数,然后再创建自己的成员变量,调用自己的构造函数

析构函数

入栈出栈顺序有关

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#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;
}

4.继承同名函数的处理

使用作用域类区别不同类下的成员函数

#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();}

5.继承同名静态成员变量的处理

静态成员变量俩种调用方式,作用域和对象名调用

但是无论是哪种调用,再同名静态成员变量继承中,都要加上作用域

#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;
}

6.多继承

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多继承构造函数的调用顺序与继承的顺序有关,谁写再前面先继承谁

#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;
}

7.菱形继承

同样存在二义性但是与多击沉不同可以通过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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/237028.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数模学习day13-典型相关分析

典型相关分析&#xff08;Canonical Correlation analysis&#xff09; 研究两组变量&#xff08;每组变量中都可能有多个指标&#xff09;之间相关关系的一种多元统计方法。它能够揭示出两组变量之间的内在联系。 注&#xff1a;本文源于数学建模学习交流相关公众号观…

使用JGit拉取代码提示未授权not authorized

原因&#xff1a;2021年8月13日后不支持密码登录&#xff0c;需要使用token验证 调用时候需要先去git仓库创建个人令牌 需要在安全中心创建个人token&#xff0c;使用token名称作为账号&#xff0c;使用token作为密码。 另&#xff1a; Github克隆仓库的三种方式对比&#xff…

哪里能找到好用的PPT模板?12个免费模板网站让你畅快办公!

你是否有过这样的经历&#xff0c;在准备重要会议或者演讲的时候&#xff0c;为找不到合适的PPT模板而困扰&#xff1f;或是在网上漫无目的地搜寻&#xff0c;结果收获的是设计平淡无奇的PPT模板&#xff1f; 如果你有同样的疑问&#xff0c;那么你来对地方了&#xff01;在这…

基于嵌入式的智能台灯系统

基于嵌入式的智能台灯系统 功能说明 通过微信小程序控制台灯的亮灭及亮度。采集温湿度传到微信小程序上&#xff0c;台灯可以显示实时北京时间。 功能展示 01智能台灯演示 Mqtt服务器 http://www.yoyolife.fun/iot&#xff1a;Mqtt服务器&#xff0c;我是在这里注册的&#x…

WPF实现右键选定TreeViewItem

在WPF中&#xff0c;TreeView默认情况是不支持右键选定的&#xff0c;也就是说&#xff0c;当右键点击某节点时&#xff0c;是无法选中该节点的。当我们想在TreeViewItem中实现右键菜单时&#xff0c;往往希望在弹出菜单的同时选中该节点&#xff0c;以使得菜单针对选中的节点生…

程序员如何弯道超车?周末有奇效

作为一名程序员&#xff0c;不断提升自己的技能和知识是至关重要的。然而&#xff0c;在繁忙的工作日常中&#xff0c;很难有足够的时间和精力来学习新技术或深入研究。因此&#xff0c;周末成为了一个理想的时机&#xff0c;可以专注于个人发展和技能提升。所以程序员如何利用…

LNMP架构及应用部署

众所周知&#xff0c;LAMP平台是目前应用最为广泛的网站服务器架构&#xff0c;其中的“A”对应着Web服务软件Apache HTTP Server。随着Nginx在企业中的使用越来越多&#xff0c;LNMP架构也受到越来越多Linux系统工程师的青睐 1.1 构建LNMP网站平台 就像构建LAMP平台一样&…

Java实现在线编辑预览office文档

文章目录 1 在线编辑1.1 PageOffice简介1.2 前端项目1.2.1 配置1.2.2 页面部分 1.3 后端项目1.3.1 pom.xml1.3.2 添加配置1.3.3 controller 2 在线预览2.1 引言2.2 市面上现有的文件预览服务2.2.1 微软2.2.2 Google Drive查看器2.2.3 阿里云 IMM2.2.4 XDOC 文档预览2.2.5 Offic…

Vue3-完成任意组件之间的传值

一、props&#xff08;只限于父子之间&#xff0c;不嫌麻烦可以不断传&#xff09; 父给子传值&#xff0c;子接收defineProps 父给子传事件&#xff0c;子接收defineProps&#xff0c;并触发事件的时候传值&#xff0c;然后父通过事件的回调函数拿到子传的值 二、mitt&#…

【UE Niagara学习笔记】07 - 火焰的热变形效果

目录 效果 步骤 一、创建热变形材质 二、添加新的发射器 2.1 设置粒子材质 2.2 设置粒子初始大小 2.3 设置粒子持续生成 三、修改材质 四、设置粒子效果 在上一篇博客&#xff08;【UE Niagara学习笔记】06 - 制作火焰喷射过程中飞舞的火星&#xff09;的基础上继续…

Python自动化我选DrissionPage,弃用Selenium

DrissionPage 是一个基于 python 的网页自动化工具。 它既能控制浏览器&#xff0c;也能收发数据包&#xff0c;还能把两者合而为一。 可兼顾浏览器自动化的便利性和 requests 的高效率。 它功能强大&#xff0c;内置无数人性化设计和便捷功能。 它的语法简洁而优雅&#x…

VMware workstation安装debian-12.1.0虚拟机并配置网络

VMware workstation安装debian-12.1.0虚拟机并配置网络 Debian 是一个完全自由的操作系统&#xff01;Debian 有一个由普罗大众组成的社区&#xff01;该文档适用于在VMware workstation平台安装debian-12.1.0虚拟机。 1.安装准备 1.1安装平台 Windows 11 1.2软件信息 软…

【野火i.MX6NULL开发板】挂载 NFS 网络文件系统

0、前言 参考资料&#xff1a; &#xff08;误人子弟&#xff09;《野火 Linux 基础与应用开发实战指南基于 i.MX6ULL 系列》PDF 第22章 参考视频&#xff1a;&#xff08;成功&#xff09; https://www.bilibili.com/video/BV1JK4y1t7io?p26&vd_sourcefb8dcae0aee3f1aab…

HarmonyOS4.0——ArkUI应用说明

一、ArkUI框架简介 ArkUI开发框架是方舟开发框架的简称&#xff0c;它是一套构建 HarmonyOS / OpenHarmony 应用界面的声明式UI开发框架&#xff0c;它使用极简的UI信息语法、丰富的UI组件以及实时界面语言工具&#xff0c;帮助开发者提升应用界面开发效率 30%&#xff0c;开发…

k8s的集群调度

1、scheduler&#xff1a;负责调度资源&#xff0c;把pod调度到指定的node节点 &#xff08;1&#xff09;预算策略 &#xff08;2&#xff09;优先策略 2、List-watch &#xff08;1&#xff09;在k8s集群中&#xff0c;通过List-watch的机制进行每个组件的协作&#xff0…

Rust 最新版1.75.0升级记

升级方法 稳定版 当前版本号1.70.0 升级稳定版&#xff0c;需要用上参数 stable C:\>rustup update stable info: syncing channel updates for stable-x86_64-pc-windows-msvc info: latest update on 2023-12-28, rust version 1.75.0 (82e1608df 2023-12-21) info: d…

WPF真入门教程27--项目案例--设备数据实时监测

1、上图看效果 今天要做的一个案例是这样的效果&#xff0c;它能实时监测车间设备有关数据&#xff0c;并以表格和图形显示在界面上&#xff0c;这个比上个案例要复杂些&#xff0c;颜值也高些&#xff0c;通过这个来巩固wpf的技能&#xff0c;用到了命令绑定&#xff0c;样式…

Arcgis像元统计数据

目录 单幅影像统计多幅影像统计 单幅影像统计 现有一幅NDVI影像&#xff0c;如何知道影像中NDVI的分布情况呢&#xff1f; 先栅格转点&#xff0c;然后在属性表中查看汇总情况 还有一种方法就是在ENVI中打开&#xff0c; -0.3-0.338占据了99% 多幅影像统计 现有多幅NDVI影…

【Python】Python语言 3小时速通(有C语言基础版)

python从入门到实践 变量 message"hello world"并不需要指出变量类型 方法 tittle()#以首字母大写的形式输出单词upper()#全部大写输出lower()#全部小写输出存储数据时经常使用lower&#xff0c;因为无法确保数据是大写还是小写 rstrip()#输出删除字符串尾部多余…

Mac M1 Parallels CentOS7.9 Install Jenkins

官网: https://www.jenkins.io/ 一、Install & Check Java Env Oracle官网下载Java: https://www.oracle.com/cn/ # 拷贝到Jenkins服务器 scp Downloads/jdk-8u391-linux-aarch64.tar.gz root10.211.55.34:~# 解压 mkdir -p /opt/java && tar -zxvf jdk-8u391-li…