C++学习基础版(一)
目录
一、C++入门
1、C和C++的区别
2、解读C++程序
3、命名空间
4、输入输出
(1)cout输出流
(2)endl操纵符
(3)cin输入流
二、C++表达式和控制语句
1、数据机构
特别:布尔类型bool
2、算数运算符
练习一:拆分位数
练习二:分段函数求值
3、bool类型
4、C++自增++和自减--运算符
5、赋值运算符=
6、关系运算符
7、逻辑运算符
8、if选择结构
练习:水仙花数
9、switch选择结构
10、while循环结构
练习:判断素数
11、do while循环
练习:N以内累加求和
12、for循环
练习:计算1~N之间所有奇数之和
三、C++函数调用与重载、内联
1、函数调用的方法
练习:自定义函数之字符串反转
2、带默认形参值的函数
3、函数重载
4、函数模板
5、内联函数inline
四、类和对象
1、类的定义
2、对象的建立和使用
(1)对象的建立
(2)对象的指针
(3)对象的使用
3、构造函数(Constructor)【系统可自动生成】
4、析构函数(Destructor)【系统可自动生成】
5、拷贝构造函数【系统可自动生成】
6、浅拷贝与深拷贝
7、this指针
8、友元函数的使用方法
练习:求两点之间距离的程序(友元函数)
9、友元类的使用方法
练习:求两点之间距离的程序(友元类)
10、常数据的使用及初始化
常数据成员的使用及初始化
(1)常数据成员
(2)常对象
(3)常成员函数
其他易混淆的内容
1、形参是什么?
前半部分学习下来,觉得C和C++虽然很多地方都是类似的,但有些还是具有很大差异的。写的这些内容虽然都是已有的知识,甚至有的都是直接直接复制过来的,但是理解后再写总结和自己的心得,代码敲下来,修改bug,运行出来,还是和只匆匆看过有些区别的。
一、C++入门
1、C和C++的区别
C++语言是在C语言基础之上为支持面向对象而设计的这么一套程序语言,而面向对象在C++中的体现就是类的机制,所以C++也称作是“带类的C语言”。
C++中的类类似于C语言的结构体,但不同的是:在C++的类中,不仅包含多个基本变量类型,还可以包含很多函数。
举个简单的对号例子:
C语言中定义一个具有5个元素的结构体:
struct stu
{int num;char sex;int math_score;int en_score;int c_score;};
int main()
{struct stu A;return 0;
}
在C++中使用类定义一个相同的内容:
class stu
{int num;char sex;int math_score;int en_score;int c_score;int total_score(){return math_score+en_score+c_score;};};
int main()
{class stu A;return 0;
}
C++类中的成员变量叫做属性,类里的函数叫做方法,即类拥有属性与方法两部分。
2、解读C++程序
#include<iostream>
using namespace std;
int main()
{cout<<"Nice to meet you!"<<endl;return 0;
}
首先程序第一行,文件包含iostream的标准库。
第二行,声明使用一个叫std的命名空间。
第三至七行,一个C++程序有且仅有一个的主函数体,cout行负责打印一段话。
3、命名空间
using namespace std;是为了解决多个模块间命名冲突的问题。
C++把相同的名字都放到不同的空间里,来防止名字的冲突。
例如标准C++库提供的对象都存放在std这个标准名字空中,比如cin、cout、endl,所以C++程序中都有using namespace std;
如果要使用cout输出一行的话,可以:
(1)使用上述带有using namespace std;这句话。
cout<<"Nice to meet you!"<<endl;
(2)用域限定符::来逐个制定:
#include<iostream>
int main()
{std::cout<<"Nice to meet you!"<<std::endl;return 0;
}
即cout和endl前面分别用std::指明,表示来自std。
(3)用using和域限定符一起制定用哪些名字:
#include<iostream>
using std::cout;
using std::endl;
int main()
{cout<<"Nice to meet you!"<<endl;return 0;
}
4、输入输出
C++程序中程序的输入输出,除了完全兼容C的写法,即使用printf和scanf函数来实现。
还有一套输入输出流(这里提到的流是指从某种IO设备上读入或写出的字符序列,习惯称之为“流”)
(1)cout输出流
cout输出流需要搭配<<输出操作符来使用:
其实等价于将字符串插入到cout对象里,并以cout对象作为返回值返回,所以可以连续输出多个内容。
cout<<"Hello";
将会输出hello。
(2)endl操纵符
可以直接将它插入到cout里,起输出换行的效果:
cout<<"hello"<<endl<<"bye";
输出就是两行;
(3)cin输入流
接收一个数据之前,都要先定义一个与之类型一致的变量,用来存放这个数据,然后利用cin搭配>>输入操作符,来接收用户从键盘的输入:
#include<iostream>
using namespace std;
int main()
{cout<<"hello"<<endl<<"bye"<<endl;int a;cin>>a;cout<<"get a="<<a<<endl;
}
如果没有输入的话,会自动返回a的值为0。
同时,cin也可以连续接收多个变量。
#include<iostream>
using namespace std;
int main()
{int a,b;cin>>a>>b;cout<<"get a="<<a<<endl<<"get b="<<b<<endl;
}
二、C++表达式和控制语句
主要介绍C++中的数据类型、C++算数运算符、C++bool类型、C++自增++和自减--运算符、C++赋值运算符、C++关系运算符、C++逻辑运算符、C++if选择结构、C++switch选择结构、C++while循环结构、C++do while循环以及C++for循环。
1、数据机构
C++中的数据类型,常用的有int、char、float、double分别表示整形、字符型、单精度和双精度类型,包括它们的扩展类型与C语言中用法都一致:
#include<iostream>
using namespace std;
int main()
{int num;char sex;double score1,score2,score3;cout<<"please scanf inf"<<endl;cin>>num>>sex>>score1>>score2>>score3;cout<<"ID:"<<num<<" sex:"<<sex<<" aver score"<<(score1+score2+score3)/3;return 0;
}
C++中不需要手动控制数据类型。
特别:布尔类型bool
布尔类型是一种逻辑值,关键字类型为bool,定义出来的变量只有true和false两个,分别表示真和假两个值,在内存上一般只占一个字节。
#include<iostream>
using namespace std;
int main()
{int a=3,b=7;bool r=a>b;cout<<r<<endl;cout<<sizeof(r)<<endl;return 0;
}
2、算数运算符
算数运算符(+、-、*、/、%)有五种,前四种加减乘除和C语言中用法相同。其中%是求模(取余),要求两端的运算符都必须是整数。
练习一:拆分位数
#include<iostream>
using namespace std;
int main(){int a,g,s,b;cin>>a;g=a%10;s=a%100/10;b=a/100;cout<<b<<" "<<s<<" "<<g<<endl;return 0;
}
练习二:分段函数求值
#include<iostream>
using namespace std;
int main(){int x,y;cin>>x;if(x<1){y=x;}else if(x>=1&&x<10){y=2*x-1;}else if(x>=10){ //剩下的条件约束也要写上y=3*x-11;}cout<<y;return 0;
}
3、bool类型
C语言中没有专门的逻辑值类型支持,但是C++有一个bool类型,即只能表示false(假)或true(真),这种类型只占一个字节大小。
#include<iostream>
using namespace std;
int main(){bool a=true;bool b=false;cout<<a<<endl;cout<<b<<endl;return 0;
}
输出:
1
0
bool类型输出的是数值。
4、C++自增++和自减--运算符
自增运算符(++)和自减运算符(--)都是单目运算符,即一个变量的使用。以++也就是自增运算符为例,通过位置在前在后决定是“先加再用”,还是“先用再加”:
#include<iostream>
using namespace std;
int main(){int a=3,b=6;cout<<a++<<" "<<++a<<endl;//先输出再自增,所以第一次输出后自增为4cout<<b++<<" "<<++b<<endl;return 0;
}
输出的原因是:第一次输出的时候先输出3,然后再增加为4;第二次先增加为5,然后再输出。
5、赋值运算符=
=是C语言和C++中的赋值运算符,用于变量 、数组等的赋值使用,操作方向是从右至左。
#include<iostream>
using namespace std;
int main(){int a=3,b;b=a;cout<<a<<" "<<b<<endl;return 0;
}
正确的读法也是从右往左读,如b=a应读为将a赋值给b.
6、关系运算符
C++中的关系运算符包括>、< 、>=、<=、!=、==六种。
cout<<(5<3)<<endl;cout<<(5!=1)<<endl;cout<<(5==5)<<endl;
7、逻辑运算符
C++中的逻辑运算符包括&&、||、!三种,分别表示逻辑与、逻辑或、逻辑非。
#include<iostream>
using namespace std;
int main(){int a=10,b=10,c=30,d,e;d=!c>(b-a)&&(c-b)>(b-a);e=(b-a)||(c-d)&&!(c-b-a);cout<<d<<endl;cout<<e<<endl;return 0;
}
8、if选择结构
C++中的选择结构,依旧用if选择结构、if-else选择结构、else-if多选择结构以及switch多选择结构,与C语言没有异同。
练习:水仙花数
题目描述:判断一个数是否为"水仙花数",所谓"水仙花数"是指这样的一个数:首先是一个三位数,其次,其各位数字的立方和等于该数本身。例如:371是一个"水仙花数",371=3^3+7^3+1^3.
#include<iostream>
using namespace std;
int main(){int a,g,s,b;cin>>a;g=a%10;s=a%100/10;b=a/100;if(a==g*g*g+s*s*s+b*b*b){cout<<"1";}else{cout<<"0";}return 0;
}
9、switch选择结构
C++中的switch结构同样也可以实现多种分支结构,类似else if结构,即对于多种情况时候可以根据条件让程序判断选择走哪个分支。
switch(表达式)
{ case 常量表达式1:语句1; break;case 常量表达式2:语句2; break;case 常量表达式3:语句3; break;// … … case 常量表达式n:语句n; break;default:语句n+1;break;
}
【注意】:switch括号后面没有分号
程序的执行流程是,首先执行switch后面小括号里表达式的值,然后和case后面的常量比较,看看哪个相等,一旦相等,那么就从这个case冒号后面的语句开始执行,即执行完对应的语句后,把后面case的语句也执行完,并且不再去判断case的值相等与否了。而如果比较完一遍发现所有case都不相等的话,那么就执行default后面的语句了。
10、while循环结构
C++中的循环,相较于C语言没有改变,依旧是while循环、do-while循环以及for循环三种,包括配合使用很多的break和continue使用方法上都没有什么差别。
练习:判断素数
编写一个程序判断一个数是否为素数【除了1和他本身,都能被整除】
#include<iostream>
using namespace std;
int main(){int a,i;cin>>a;for(i=2;i<a;i++){if(a%i==0){break;}}if(i>=a){ //其实就是到a本身cout<<"1"<<endl;}else{cout<<"0"<<endl;}return 0;
}
11、do while循环
与while循环不同的是,它的执行流程是,遇到do先进入循环执行一次循环体里的语句,然后再判断while里的表达式是否成立,来决定是否进入循环执行第二次。
可以看到,它的特点是无论条件成立于否,都会至少执行一次循环体里的语句。
do
{循环体语句
}while(表达式);
练习:N以内累加求和
输入一个数字N,输出N以内(包含N)的数字之和 (N不超过1000)
#include<iostream>
using namespace std;
int main(){int i=0,n,sum=0;cin>>n;do{sum+=i;i++;}while(i<=n);cout<<sum<<endl;return 0;
}
12、for循环
for循环是C++循环中的第三种循环:
for(初始化表达式1;判断表达式2;更新表达式3)
{ 循环体语句
}
程序执行遇到for循环后,首先执行初始化表达式1,然后执行判断表达式2,根据真假决定循环体是否执行,若不成立则跳出结束循环。
若成立则执行循环体里的语句,然后进而执行更新表达式3,再重新返回判断表达式2.重复上面过程。
练习:计算1~N之间所有奇数之和
#include<iostream>
using namespace std;
int main(){int i,n,sum=0;cin>>n;for(i=0;i<=n;i++){if(i%2!=0){sum+=i;}}cout<<sum;return 0;
}
三、C++函数调用与重载、内联
主要介绍C++中函数调用的用法、C++带默认形参值的函数、C++函数重载、C++函数模板以及C++内联函数inline。
1、函数调用的方法
C++中函数调用的方法与C语言相同,是在调用方函数中执行函数调用语句来实现函数调用。
练习:自定义函数之字符串反转
题目描述:写一函数,使输入的一个字符串按反序存放,在主函数中输入并输出反序后的字符串(不包含空格)。
输入一段字符串;输出翻转后的字符串;
#include<iostream>
#include<cstring>
using namespace std;
int reverse(char a[],char b[]){int i=0,n;n=strlen(a);while(a[i]!='\0'){b[n-i-1]=a[i];i++;}b[n]='\0';return 0;
}int main(){char str1[100],str2[100];cin>>str1;reverse(str1,str2);cout<<str2<<endl;return 0;
}
2、带默认形参值的函数
在C++中,允许在自定义函数的形参列表中,给形参一个默认的值,这样在调用的时候如果有实参,那么按照实参传递给形参的方法使用;若调用的时候没有指定对应的实参,则形参将使用默认值。
#include<iostream>
#include<cstring>
using namespace std;
int add(int a=5,int b=3){return a+b;
}int main(){cout<<add(30,20)<<endl;cout<<add(20)<<endl; //使用b的默认值cout<<add()<<endl; //使用a,b的默认值return 0;
}
由于参数的传递顺序是从右至左入栈,所以有默认值的参数必须在放在形参列表的最右边!另外,当函数需要提前声明时,若形参存在默认参数,则声明部分可以制定默认值,而后面的函数定义部分则不再制定默认值。
3、函数重载
对同一个功能函数而言,可能处理的对象类型不同,则需要重新实现这个函数。但是上述过程太繁琐,所以C++支持函数重载解决这个问题。
例如说,在上述的求和函数中,用来接收传入数据并求和,但是作为一个独立的模块, 如何摘掉调用方和传入什么类型的数据(可能是整型、浮点类型等),就需要针对同一个函数为不同类型的元素定制不同的函数。
因此函数重载即两个或以上的函数,函数名相同,但形参类型或个数不同,编译器根据调用方传入的参数的类型和个数,自动选择最适合的一个函数来进行绑定调用,自动实现选择。
#include<iostream>
using namespace std;
int add(int a,int b)
{cout<<"(int ,int)\t";return a+b;
}
double add(double a,double b)
{cout<<"(doble ,double)\t";return a+b;
}
double add(double a,int b)
{cout<<"(double ,int)\t";return a+b;
}
double add(int a,double b)
{cout<<"(int ,double)\t";return a+b;
}
int main()
{cout<<add(2,3)<<endl;cout<<add(2.9,15.3)<<endl;cout<<add(10,9.9)<<endl;cout<<add(11.5,5)<<endl;return 0;
}
4、函数模板
上述函数重载可以处理多种数据类型,但是可以更精简。
函数模板,是可以创建一个通用的函数,可以支持多种形参。用关键字template来定义:
template<class 类型名1,class 类型名2…>
返回值 函数名(形参表列) 模板参数表
{函数体
}
第一行的template<class 类型名1,class 类型名2…>是一句声明语句,template是定义模板函数的关键字,尖括号里可以有多个类型,前面都要用class(或者typename来定义)。
#include<iostream>
using namespace std;
template<class T1,class T2>
T1 add(T1 x,T2 y){cout<<sizeof(T1)<<" "<<sizeof(T2)<<"\t";return x+y;
}
int main(){cout<<add(10,20)<<endl;cout<<add(1.88,1.12)<<endl;cout<<add("A",2)<<endl;return 0;
}
在主函数中,实际调用时调用了三次,分别三种不用的类型传入,模板函数中的T1和T2类型将根据实际传入的类型变成具体类型,这个化成就叫做模板的实例化。
运行后,可以看出每次调用的类型T1和T2到底是什么类型,有多大字节,以及求和的结果。
5、内联函数inline
一个定义完全的函数,只有在被调用的时候,才会被分配对应的内存空间,在调用完毕后再清理释放。
因此函数的作用就是为了提高代码的可用性,提高开发效率。但如果一个函数代码不多,且被频繁调用,可以使用内联机制。即仍然使用自定义函数,但在编译的时候,把函数代码插入到函数调用处,像普通顺序执行的代码一样,从而免去函数调用的一系列过程。
在函数定义的前面加上关键字inline声明就可以了
#include<iostream>
using namespace std;
inline int max(int a,int b){return (a>b)?a:b;
}
int main(){int x,y;cin>>x>>y;cout<<max(x,y)<<endl;return 0;
}
内联函数的定义要在调用之前出现,才可以让编译器在编译期间了解上下文,进行代码替换。
四、类和对象
这一章主要介绍C++类的定义、C++对象的建立和使用、C++中的构造函数(Constructor)、C++中的析构函数(Destructor)、C++拷贝构造函数、C++浅拷贝与深拷贝、C++中的this指针、C++友元函数的使用方法、C++友元类的使用方法以及C++中常数据的使用及初始化。
1、类的定义
类其实就是一个模子,是一个变量类型,对象就是这个类型定义出来的具体的变量,就像int a;这句话,int对应类,a就对应对象。
概括的讲:类是对象的抽象和概括,而对象是类的具体和实例。
C++中类的其实就是包含函数的结构体!因为C++类里面的成员除了可以像C语言的结构体那样包含基本变量以外,还可以包含函数,前者叫做成员变量,后者叫做成员方法。[
关键字用class类定义,比如一个简单的student类:【类定义后面还有一个分号】
class Student
{
public:int num;char name[100];int score;int print(){cout<<num<<" "<<name<<" "<<score;return 0;}
};
形式上和C语言的结构体非常像,成员有变量也有函数,称之为属性和方法。
- private表示私有,被它声明的成员,仅仅能被该类里的成员访问,外界不能访问,是最封闭的一种权限;
- protected比private稍微公开一些,除了类内自己的成员可以访问外,它的子类也可以访问;
- public声明的成员,则可以被该类的任何对象访问,是完全公开的数据。
类的另一种写法:成员函数仅在类中声明函数原型,在类外定义函数体,这样就可以在类中看到所有的成员函数列表。
其中在类中声明函数原型的方法和在C语言中的函数原型声明一样,但是在类的外边定义的函数,需要类名类名加::作用域限定符表示。则,类外定义的代码如下:
class Student
{
public:int num;char name[100];int score;int print(); //类中声明print函数
};
int Student::print() //在类外定义完整的print函数{cout<<num<<" "<<name<<" "<<score;return 0;}
也就是说,在类中是事先声明的,在类外又完整的定义了,函数头部分在返回值和函数名之间用类名加::作用域限定符指明函数属于哪个类。
2、对象的建立和使用
(1)对象的建立
类就是包含函数的结构体,是一种自定义数据类型,用它定义出来的变量就是对象,这就是“对象是类的具体和实例”,定义了一个这个类的对象,也可以说实例化了一个对象。
类的使用和结构体相同,都是要访问里面的成员,都是通过类名.变量的方式来访问:
Student A;
A.num = 101;
strcpy(A.name,"dotcpp");
A.score = 100;
A.print();
这里类中的成员变量都是声明为public类型的,如果声明为private类型,则在主函数中主要通过对象.变量的方式直接访问的话就会被禁止报错,原因private类型的变量是私有类型,不允许外部访问。
对于想保护但又想控制的私有变量,通常将其声明为private类型,然后同时定义一个public类型的专门赋值的方法,由于内部成员可以访问private声明的变量,就可以在外部通过这个public的方法来间接控制这些私有的成员,来起到封装、保护的效果,而这个public类型的方法,也称之为这个类的一个外部接口。
(2)对象的指针
与普通变量一样,对象也是一片连续的内存空间,因此也可以创建一个指向对象的指针,即对象指针,存储这个对象的地址。定义方法:
类名 *指针名;
例如下述中,Student *p;定义一个clock类型的指针p【并没有建立对象,也没有调用析构函数】;
接下来就是将一个同类型的类对象地址赋值给这个指针,然后通过->来访问对象中的成员。
Student *p;
Student A;
p = &A;
p->print();
使用指针的好处:除了在赋值、访问成员的时候用以外,在传参的时候也建议用指针来传递,因为其传递的为地址,不会进行对象之间的副本赋值,从而减少内存的开销,提高效率。
(3)对象的使用
引用,是C++中一种新的类型,对象引用就是一个类对象起个别名,本质上也是把这个类对象的地址赋给了这个引用类型,两者是指向一块内存空间的。
Student A;
Student &Aq=A;
如上述,定义一个Student类型的对象,然后用&来定义一个该类类型的引用类型,并把A对象赋给Aq作为初始化。
对象引用中主要注意的地方:
- 两者必须是同类型才可以引用;
- 除非做函数的返回值或形参时,其他定义引用类型的同时就要初始化;
- 引用类型并不是建立一个对象,因此不会调用析构函数。
因此是类对象的别名,所以使用方法也和类对象一样,用别名.成员的方法进行访问,如:
Student A;
Student &Aq=A; //用引用类型时,本质还是存的地址
Aq.print();
用引用类型时,本质还是存的地址,因此无论传参定义都不会太多内存开销,有指针的优势,同时使用起来和类对象本身使用一样,再做函数实参时,直接传入引用对象就可以,不用加地址符,因此看起来更直观、方便。
3、构造函数(Constructor)【系统可自动生成】
在类里,与类名同名,且没有返回值的一个函数,只要我们定义一个类的对象,系统就会自动调用它,进行专门的初始化对象用,而大多数情况下,因为没有定义构造函数,系统会默认生成一个默认形式、隐藏着的构造函数,这个构造函数的函数体是空着的,因此不具有任何功能。
因此,如果用户定义了至少一个构造函数,系统就不会再自动生成,而是根据用户定义的构造函数进行最匹配的一个调用。
#include<iostream>
#include<cstring>
using namespace std;
class Student
{private:int num;//学号char name[100];//名字int score;//成绩public:Student(int n,char *str,int s);int print();int Set(int n,char *str,int s);
};
Student::Student(int n,char *str,int s)
{num = n;strcpy(name,str);score = s;cout<<"Constructor"<<endl;
}
int Student::print()
{cout<<num<<" "<<name<<" "<<score;return 0;
}
int Student::Set(int n,char *str,int s)
{num = n;strcpy(name,str);score = s;
}
int main()
{Student A(100,"lilei",11);A.print();return 0;
}
由于已经在类中定义了一个带默认参数的构造函数,则系统不会再自动生成,这个时候定义对象的时候也需要传入三个默认初始值,因为构造函数可以重载,系统会找最匹配的一个函数,但如果定义时不是带默认参数的构造函数,则会报错.
4、析构函数(Destructor)【系统可自动生成】
构造函数是类对象在创建自动调用的,在对象销毁时也会自动调用一个函数,它也和类名同名,也没有返回值,名字前有一个波浪线~,用来区分构造函数,它的作用主要是用做对象释放后的清理善后工作。它就是析构函数。
与构造函数相同的是,与类名相同,没有返回值,如果用户不定义,系统也会自动生成一个空的析构函数。而一旦用户定义,则对象在销毁时自动调用。
与构造函数不同的是,虽然两个都为公开类型。构造可以重载,可以有多个构造函数,而析构函数却不能重载,但它可以是虚函数,一个类只能有一个析构函数。
#include<iostream>
#include<cstring>
using namespace std;
class Student
{private:int num;//学号char name[100];//名字int score;//成绩public:Student(int n,char *str,int s);~Student(); //先声明int print();int Set(int n,char *str,int s);
};
Student::Student(int n,char *str,int s)
{num = n;strcpy(name,str);score = s;cout<<"Constructor"<<endl;
}Student::~Student() //析构函数没有参数
{cout<<num<<" "<<name<<" "<<score<<" ";cout<<"destructor"<<endl;
}int Student::print()
{cout<<num<<" "<<name<<" "<<score;return 0;
}
int Student::Set(int n,char *str,int s)
{num = n;strcpy(name,str);score = s;
}
int main()
{Student A(100,"lilei",11);Student B(1111,"hanmeimei",59);return 0;
}
输出原因:
- 首先创建了对象 A,然后创建了对象 B。因此,首先输出的是 A 对象的构造函数消息 "Constructor"。接着,输出的是 B 对象的构造函数消息 "Constructor"。
- 然后,程序结束时,对象 B 先被销毁,所以先输出 B 对象的析构函数消息,即 "1111 hanmeimei 59 destructor"。接着,对象 A 被销毁,输出 A 对象的析构函数消息,即 "100 lilei 11 destructor"。
- A和B对象同属局部对象,也在栈区存储,也遵循“先进后出”的顺序!
- 在 main 函数中,先创建的对象会后被销毁,所以先输出后创建的对象的析构函数消息。【相当于一个栈的形式】
5、拷贝构造函数【系统可自动生成】
在C++中,与类名同名,且形参是本类对象的引用类型的函数,叫做拷贝构造函数(Copy Constrctor),与构造函数一样,当不主动定义的时候,系统也会自动生成一个,进行两个对象成员之间对应的简单赋值,用来初始化一个对象。
#include<iostream>
using namespace std;
#define PI 3.1415
class Circle
{private:double R;public:Circle(double R);Circle(Circle &A);double area();double girth();
};
Circle::Circle(double R)
{cout<<"Constructor"<<endl;this->R = R;
}
Circle::Circle(Circle &A)
{cout<<"Copy Constructor"<<endl;this->R = A.R;
}
double Circle::area()
{return PI*R*R;
}
double Circle::girth()
{return 2*PI*R;
}
int main()
{Circle A(5);Circle B(A);return 0;
}
在构造函数Circle::Circle(double r)中,this->r = r;的意思是将传入构造函数的参数r赋值给当前对象的成员变量r。这样做是为了区分参数r和成员变量r,以确保正确赋值。
在拷贝构造函数Circle::Circle(Circle &A)中,this->r = A.r;的意思是将传入的对象A的成员变量r的值赋值给当前对象的成员变量r。这样可以创建一个新的Circle对象,并将传入的Circle对象A的属性复制到新对象中。
本例中定义了一个Circle圆形类,分别定义了带参数的构造函数和拷贝构造函数,然后在主函数中定义A对象,并传入初始值,调用带参数的构造函数。及定义B对象,通过A对象来初始化B对象。
第一次定义的A对象调用带参数的构造函数,第二个B对象由于是通过A对象来初始化,所以调用拷贝构造函数。
默认的拷贝构造函数仅仅是做简单的赋值,有些情况则要出现问题,这就涉及到深拷贝与浅拷贝。
6、浅拷贝与深拷贝
在前面讲到的贝构造函数的例子Circle类中,拷贝的策略都是与系统默认的策略一致,即把原有对象中成员依次拷贝给新对象中对应的成员。但简单的将所有情况都按照这种简单的方式初始化,可能出现问题。
例如:如果在刚刚的Circle类的成员变量中加一个指针成员,初始化需要动态开辟内存,这时候就会出现问题:
#include<iostream>
#include<cstring>
using namespace std;
#define PI 3.1415
class Circle{private:double r;char *str;public:Circle(double r,char *str);~Circle();double area();double length();
};
Circle::~Circle(){delete []str;
}Circle::Circle(double r,char *str){cout<<"constructor"<<endl;this->r=r;this->str=new char[strlen(str)+1];strcpy(this->str,str);cout<<this->r<<" "<<this->str<<endl;
}double Circle::area(){return PI*r*r;
}double Circle::length(){return 2*PI*r;
}int main(){Circle A(5,"old class");Circle B(A);return 0;
}
输出结果:
默认的拷贝构造函数仅仅是进行数据赋值,并不能为指针开辟内存空间。
例如This->str = str;本质上是两个指针指向一块堆空间。那么两个对象回收的时候,会调用自己的析构函数,释放这块内存空间,由于两个对象要调用两次,即delete两次,就会出现错误。
所以,当类中有指针类型时,依靠默认的拷贝构造函数的方法,已经无法满足需求,必须定义一个特定的拷贝构造函数,即不仅可以进行数据的拷贝,也可以为成员分配内存空间,实现真正的拷贝,也叫做深拷贝,这就是深拷贝构造函数。
#include<iostream>
#include<cstring>
using namespace std;
#define PI 3.1415
class Circle{private:double r;char* str;public:Circle(double r,char *str);Circle(Circle &A);~Circle();double area();double length();
};Circle::~Circle(){delete []str;cout<<"call Destructor"<<endl;
}Circle::Circle(Circle &A){cout<<"Copy Constructor"<<endl;this->r=A.r;this->str= new char[strlen(A.str)+1];strcpy(this->str,A.str);
}Circle::Circle(double r,char *str){cout<<"constructor"<<endl;this->r=r;this->str=new char[strlen(str)+1];strcpy(this->str,str);
}double Circle::area(){return PI*r*r;
}double Circle::length(){return 2*PI*r;
}int main(){Circle A(5,"old class");Circle B(A);return 0;
}
其中可能会出现错误的地方:在构造函数Circle::Circle(double r, char *str)中,strcpy(this->str,A.str);这行代码中的A.str应该改为str,因为str是当前构造函数的参数,而不是之前定义的对象A的成员变量。
所以,在赋值之前开辟足够的内存空间,来真正完成完整的拷贝,这就是“深拷贝”。
7、this指针
因为只要定义一个类,系统就会预定义个名字叫做this名且指向当前对象的指针。
当在类的成员函数中使用关键字"this"时,它指的是当前对象的指针,而不是类本身的指针或类的名称。假如定义了一个Circle类,并不会预定义一个名为"this"的指针。可以在Circle类的成员函数中使用关键字"this"来引用当前对象的成员变量和成员函数。
例如,如果在Circle类的成员函数中使用语句this->r = r;,它表示将传入的参数r赋值给当前对象的成员变量r。
一个时钟类的一个成员函数,用来设置时间传值的代码:
int Clock::SetTime(int h,int m,int s)
{H = h;M = m;S = s;
}
可以看到Clock类本身的成员变量为H、M、S,要用外部传来的值给它们三个赋值,为了区别它们,把形参定义成小写(这里的形参是h/m/t)。
int Clock::SetTime(int h,int m,int s)
{this->H = h;this->M = m;this->S = s;
}
int Clock::SetTime(int h,int m,int s)
{(*this).H = h;(*this).M= m;(*this).S= s;
}
以上两种表述都是正确的。
this指针可以用来区分本对象与外部变量。
因为当一个对象调用其成员函数的时候,即便程序中有多个该类的对象,但成员函数的代码也仅有一份,所以为了区分它们是哪个对象调用的成员函数,编译器也是转化成this->成员函数这种形式来使用的。
8、友元函数的使用方法
类中的私有成员,只有被类里的成员函数访问,在类外是不能访问的。类中的私有成员,只有被类里的成员函数访问,在类外是不能访问的。
但可能存在,在类外还想访问私有成员的情况,这时候就可以把外部的函数声明为友元类型,赋予它可以访问类内私有成员的权利。
友元的对象,可以是全局的一般函数,也可以是其他类里的成员函数,这种叫做友元函数。不仅如此,友元还可以是一个类,这种叫做友元类。
- 友元函数:是在类定义中声明为友元的非成员函数。声明为友元函数的函数可以直接访问类的私有成员,就像是类的成员函数一样。然而,友元函数并不能使用类内的this指针,因为它不是类的成员函数,无法访问特定的对象。
- 友元类:是在类定义中声明为友元的另一个类。声明为友元类的类可以访问该类的私有成员。与友元函数类似,友元类也不能使用类内的this指针,因为它不是类的成员函数,无法访问特定的对象。
- 友元关系不具备继承性。父类的友元并不会自动成为子类的友元,即父类的朋友不一定是子类的朋友。每个类的友元关系都是独立的,不会被继承。
在类内对友元函数进行声明,并在之前加上friend关键字。
练习:求两点之间距离的程序(友元函数)
#include<iostream>
#include<math.h>
using namespace std;
class Point{private:double x,y;public:Point(double a,double b){x=a;y=b;}int getpoint(){cout<<"("<<x<<","<<y<<")"<<endl;return 0;}friend double distance(Point &a,Point &b);
};
double distance(Point &a,Point &b){double xx,yy;xx=a.x-b.x;yy=a.y-b.y;return sqrt(xx*yy+yy*yy);
}
int main(){Point A(2.0,3.0);Point B(3.0,5.0);double dis;dis=distance(A,B);cout<<dis<<endl;return 0;
}
实现求两点之间距离的函数为外部的一般函数,由于需要访问类内的私有成员,所以把它在类内声明成frined友元类型,见Point类内的最后一行。
9、友元类的使用方法
当一个类声明另一个类为友元类时,被声明为友元的类(友元类)可以访问声明它为友元的类的私有成员。这意味着友元类可以直接访问被授权类的私有成员,就好像它们是自己的成员一样。
为了声明一个类为友元类,需要在被授权类的定义中使用friend
关键字来声明友元类。这样一来,友元类就可以访问被授权类的私有成员。
例如:
class A {
private:int privateDataA;public:A() : privateDataA(0) {}friend class B; // 声明类B为类A的友元类
};class B {
public:void accessPrivateDataA(A& obj) {obj.privateDataA = 42; // 友元类B可以直接访问类A的私有成员privateDataA}
};int main() {A objA;B objB;objB.accessPrivateDataA(objA); // 通过友元类B访问类A的私有成员return 0;
}
在上述示例中,类A声明了类B为友元类。这意味着类B可以直接访问类A的私有成员privateDataA。在类B的成员函数accessPrivateDataA中,通过传递类A的对象引用,可以修改类A的私有成员privateDataA的值。
友元类的声明使得类B在访问类A的私有成员时不受访问级别限制。这种机制可以用于在特定情况下允许一些类之间共享私有信息。
练习:求两点之间距离的程序(友元类)
#include<iostream>
#include<math.h>
using namespace std;
class Point{private:double x,y;public:Point(double a,double b){x=a;y=b;}int getpoint(){cout<<"("<<x<<","<<y<<")"<<endl;return 0;}friend class Tool;
};class Tool{public:double getx(Point &A){cout<<A.x<<endl;return A.x;}double gety(Point &A){cout<<A.y<<endl;return A.y;}double dis(Point A){cout<<sqrt(A.x*A.x+A.y*A.y)<<endl;return sqrt(A.x*A.x+A.y*A.y);}
};int main(){Point A(2.0,3.0);Tool T;T.getx(A);T.gety(A);T.dis(A);return 0;
}
定义了一个工具类,可以获取一个点类的横、纵坐标以及求出这个点距离原点的距离,由三个方法实现,封装到一个类Tool里。
优点:更方便快捷的访问类内的私有成员。
缺点:打破了C++中的封装思想。
10、常数据的使用及初始化
常数据成员的使用及初始化
“常”的关键字是const,是不可以被改变的,比如用const修饰的一个变量就成了常变量,这个值不可被更改。
C++中,const除了可以修饰一般的变量为常变量之外,还可用于修饰某个对象,变成常对象。以及可以修饰类的数据成员和成员函数,分别叫做类的常数据成员和常成员函数。
(1)常数据成员
定义的格式为:
数据类型 const 数据成员名;
或
const 数据类型 数据成员名;
被const修饰的成员则必须进行初始化,并且不能被更改,而初始化的方式则是在类的构造函数的初始化列表里进行的。
另外,有一个特殊情况,如果成员是static类型,即静态常数据成员,因为是静态的属性,初始化则需在类外进行初始化。
#include<iostream>
using namespace std;
class Clock
{
private:const int h; //修饰h为常类型成员const int m; //修饰m为常类型成员int const s; //和上面两种用法都可以static const int x;
public:Clock(int a,int b,int c):h(a),m(b),s(c){cout<<"Constrctor! Called"<<endl;}int ShowTime(){cout<<h<<":"<<m<<":"<<s<<endl;return 0;}int GetX(){cout<<x<<endl;return 0;}
};
const int Clock::x = 99;
int main()
{Clock A(12,10,30);A.ShowTime();A.GetX();return 0;
}
其中如果想要重新定义一个对象B且变量是通过输入的形式,并完成函数调用:
int main()
{/*insert start*/int x,y,z;cin>>x>>y>>z;Clock B(x,y,z);B.ShowTime();B.GetX();/*insert end*/Clock A(12,10,30);A.ShowTime();A.GetX();return 0;
}
(2)常对象
C++中可以把一个对象声明为const类型,即常对象。这样声明之后,这个对象在整个生命周期中就不可以再被更改,所以在定义的时候要由构造函数进行初始化。
将对象声明为const
时,它的成员变量被视为只读,不允许对其进行修改。这种限制可以提供一些重要的优势,例如确保对象的数据不会被意外改变,或者在函数参数中传递对象时,保证对象的状态不会被修改。在声明常对象时,必须在声明的同时进行初始化,因为常对象的值一旦被初始化,就不能再被修改。
定义格式如下:
类型 const 对象名;
或
const 类型 对象名;
还是上述的那个例子:
#include<iostream>
using namespace std;
class Clock
{
private:const int h; //修饰h为常类型成员const int m; //修饰m为常类型成员int const s; //和上面两种用法都可以int x;
public:Clock(int a,int b,int c):h(a),m(b),s(c){x=99;cout<<"Constrctor! Called"<<endl;}int ShowTime(){cout<<h<<":"<<m<<":"<<s<<endl;return 0;}int GetX() const{//x=99;cout<<x<<endl;return 0;}
};int main()
{const Clock A(12,10,30);const Clock B(14,20,50);//A = B;//A.ShowTime();A.GetX();return 0;
}
其中主函数中注销的那两句,在正常编译的时候会报错,因为:①A为常对象不可以被赋值;②常对象不可以访问类中的非常成员函数,只能访问常成员函数,但是showtime()函数是非常成员函数。
(3)常成员函数
一个类中的成员函数被const修饰后,就变成了常成员函数,常成员函数的定义如下:
返回类型 函数名(形参表列) const;
需要注意:
- 常成员函数的定义和声明部分都要包含const;
- 常成员函数只能调用常成员函数,而不能调用非常成员函数,访问但不可以更改非常成员变量。
#include<iostream>
using namespace std;
class Clock
{
private:const int h; //修饰h为常类型成员const int m; //修饰m为常类型成员int const s; //和上面两种用法都可以int x;
public:Clock(int a,int b,int c):h(a),m(b),s(c){x=99;cout<<"Constrctor! Called"<<endl;}int ShowTime(){cout<<h<<":"<<m<<":"<<s<<endl;return 0;}int GetX() const{//x=99;cout<<x<<endl;return 0;}
};int main()
{const Clock A(12,10,30);A.GetX();return 0;
}
如果使用getx()函数中的x=99;语句,就会编译错误,因为常成员函数只能访问但不可以更改非常成员变量。
其他易混淆的内容
1、形参是什么?
形参(Formal Parameter)是函数或方法定义中声明的参数,用于接收调用该函数或方法时传递的实际参数(实参)。形参是函数或方法的局部变量,在函数或方法内部使用。
当定义函数或方法时,可以指定一个或多个形参,以便在函数或方法内部使用这些参数进行操作。形参的目的是为了传递数据给函数或方法,使其能够执行相应的任务。
形参的声明包括参数的类型和参数的名称,例如:
void myFunction(int x, double y, char z);
在上述函数声明中,x、y和z是形参,它们的类型分别为int、double和char。这意味着当调用myFunction函数时,必须提供一个int类型的值、一个double类型的值和一个char类型的值作为实参。
实参(Actual Argument)是在函数或方法调用时传递给形参的实际值。实参可以是常量、变量、表达式或函数的返回值,只要它们的类型和顺序与函数或方法定义中的形参匹配即可。
在函数或方法调用时,实参的值被传递给对应的形参,并在函数或方法内部使用。
例如:
void printSum(int a, int b) {int sum = a + b;cout << "Sum: " << sum << endl;
}int main() {int x = 5;int y = 3;printSum(x, y); // 调用printSum函数,x和y是实参return 0;
}
在上述示例中,printSum函数有两个形参a和b,在函数调用printSum(x, y)中,变量x和y作为实参传递给形参a和b,并在函数内部使用它们计算并打印出它们的和。
相关文章:

C++学习基础版(一)
目录 一、C入门 1、C和C的区别 2、解读C程序 3、命名空间 4、输入输出 (1)cout输出流 (2)endl操纵符 (3)cin输入流 二、C表达式和控制语句 1、数据机构 特别:布尔类型bool 2、算数运…...
Rust 双向链表 LinkedList 和安全删除元素的方法
一、LinkedList 基本用法 在Rust中,LinkedList 是标准库中 std::collections 模块提供的一个双向链表实现。这个双向链表在每个节点中都保存了其前一个和后一个节点的引用,允许在链表的任一端进行有效的添加和移除操作。 以下是一个简单的示例…...
Android 开发中 Gradle 使用详解:构建、配置与优化技巧
文章目录 1. 基本概念2. 配置构建脚本2.1 项目级构建脚本2.2 模块级构建脚本 3. 自定义构建变体和应用 flavorDimensions4. 多模块项目4.1 创建模块4.2 配置模块依赖 5. 使用 Gradle 插件6. 使用 Gradle 命令 Gradle 是一种先进的构建工具,它被广泛应用于 Android 开…...

聚道云助力:易快报CDP无缝对接,登录同步一步到位!
一、客户介绍 某企业咨询有限公司是一家专注于为企业提供全方位、高质量咨询服务的领先机构。该公司致力于将先进的管理理念和实践经验与企业实际需求相结合,助力企业实现可持续发展。无论是战略规划、组织优化、人力资源管理,还是市场营销、财务管理等…...
Java解决幸运数字
Java解决幸运数字 01 题目 哈沙德数是指在某个固定的进位制当中,可以被各位数字之和整 除的正整数。 例如 126 是十进制下的一个哈沙德数,因为 (126)10 mod (1 2 6) 0; 126 也是8进制下的哈沙德 数,因为(126)10 (176)8,(126)10…...
将一个nextjs项目部署到vercel
注:下面均为AI创作(本人已验证该流程可行) 将一个 Next.js 项目部署到 Vercel 是一个相对直接的过程,因为 Vercel 是由同一个团队开发的,专门为 Next.js 优化。以下是部署一个 Next.js 项目到 Vercel 的基本步骤&…...

RocketMQ学习笔记:分布式事务
这是本人学习的总结,主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、分布式事务的难题2、解决方式2.1、半事务消息和事务回查2.2、代码样例2.2.1、TransactionListener2.2.2、TransactionMQProducer2.2.3、MessageListenerConcurrently2.2.4、流程图 1、分布…...

单臂路由和三层交换机
目录 一.单臂路由 1.单臂路由的工作原理 2.单臂路由的配置 2.1画出拓扑图 2.2配置PC 2.3配置交换机 2.4配置路由器 2.5测试 二.三层交换机 1.三层交换机的概述 2.三层交换机的配置 2.1画出拓扑图 2.2配置PC 2.3配置二层交换机 2.4配置三层交换机 2.5测试 3.拓展 三.总结 一.…...

红岩思维导图的制作软件,分享4款热门的!
红岩思维导图的制作软件,分享4款热门的! 在当今信息爆炸的时代,思维导图作为一种有效的知识整理和思维拓展工具,受到了广大用户的青睐。红岩思维导图以其独特的风格和实用性,成为了许多人学习和工作中的得力助手。那么…...
es 集群开机自动启动
前面搭建了 es 集群,但是每次机器重启 都需要手动启动,很麻烦,所以这里介绍一下开机自动启动 首先使用 root 用户 es : 执行以下命令 vim /etc/init.d/elasticsearch 将以下内容 cv 进去 #!/bin/bash #chkconfig: 345 63 …...
使用JMeter从JSON响应的URL参数中提取特定值
在使用Apache JMeter进行API测试时,我们经常需要从JSON格式的响应中提取特定字段的值。这可以通过使用JMeter内置的JSON提取器和正则表达式提取器来完成。以下是一个具体的例子,展示了如何从一个JSON响应中提取rowId的值,同时处理字符串终止符…...

汽车电子行业知识:自动驾驶系统结构和各模块功能
文章目录 2.自动驾驶系统结构和各模块功能2.1.自动驾驶系统结构2.2.车载传感器2.2.1.激光雷达2.2.2.毫米波雷达2.2.3.超声波雷达2.2.4.摄像头2.2.5.GNSS2.2.6. IMU2.2.7.多传感器融合 2.3.各功能模块2.3.1.高精度地图2.3.2.定位2.3.3.感知2.3.4.决策2.3.5.规划2.3.6.控制2.3.7.…...

Oracle参数文件详解
1、参数文件的作用 参数文件用于存放实例所需要的初始化参数,因为多数初始化参数都具有默认值,所以参数文件实际存放了非默认的初始化参数。 2、参数文件类型 1)服务端参数文件,又称为 spfile 二进制的文件,命名规则…...

鸿蒙(HarmonyOS)Navigation如何实现多场景UI适配?
场景介绍 应用在不同屏幕大小的设备上运行时,往往有不同的UI适配,以聊天应用举例: 在窄屏设备上,联系人和聊天区在多窗口中体现。在宽屏设备上,联系人和聊天区在同一窗口体现。 要做好适配,往往需要开发…...

PTGui图像拼接实验
1 PTGui图像拼接实验 1.1 概述 图像拼接技术就是将数张有重叠部分的图像(可能是不同时间、不同视角或者不同传感器获得的)拼成一幅无缝的全景图或高分辨率图像的技术 图像配准(image alignment)和图像融合是图像拼接的两个关键…...

C++|类封装、类的分文件编写练习:设计立方体类、点和圆的关系
文章目录 练习案例1:设计立方体类CPP代码 练习案例2:点和圆的关系CPP代码 代码总结类的分文件编写 练习案例1:设计立方体类 设计立方体类(Cube) 求出立方体的面积和体积 分别用全局函数和成员函数判断两个立方体是否相等。 CPP代码 class Cube { pub…...

大数据开发扩展shell--尚硅谷shell笔记
大数据开发扩展shell 学习目标 1 熟悉shell脚本的原理和使用 2 熟悉shell的编程语法 第一节 Shell概述 1)Linux提供的Shell解析器有: 查看系统中可用的 shell [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh/bin/bash/sbin/nologin/bin/dash/bin/t…...

考研数学|《1800》《1000》《880》《660》最佳搭配使用方法
直接说结论:基础不好先做1800、强化之前660,强化可选880/1000题。 首先,传统习题册存在的一个问题是题量较大,但难度波动较大。《汤家凤1800》和《张宇1000》题量庞大,但有些题目难度不够平衡,有些过于简单…...
【GameFramework框架内置模块】17、声音(Sound)
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群:398291828大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录: https://blog.csdn.net/q764424567/article/details/1…...

视频记录历史播放位置效果
简介 每次打开页面视频从上一次的播放位置开始播放 利用lodash库做节流 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...