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…...

Request Response
简介 Request(请求) & Response(响应) 浏览器会向服务器发送请求数据,服务器也需要返回响应数据给浏览器,因此我们需要设置对应的类来代表请求数据和响应数据,且Servlet中的service方法就需…...

How to convert .py to .ipynb in Ubuntu 22.04
How to convert .py to .ipynb in Ubuntu 22.04 jupyter nbconvertp2j 最近看到大家在用jupyter notebook,我也试了一下,感觉还不错,不过,也遇到了一些问题,比方说,我有堆的.py文件,如果要一个一…...

【prometheus-operator】k8s监控集群外redis
1、部署exporter GitHub - oliver006/redis_exporter: Prometheus Exporter for Redis Metrics. Supports Redis 2.x, 3.x, 4.x, 5.x, 6.x, and 7.x redis_exporter-v1.57.0.linux-386.tar.gz # 解压 tar -zxvf redis_exporter-v1.57.0.linux-386.tar.gz # 启动 nohup ./redi…...

MySQL索引(图文并茂)
目录 一、索引的概念 二、索引的作用 三、创建索引的原则依据 四、索引的分类和创建 1、索引的分类 2、索引的创建 2.1 普通索引 2.1.1 直接创建索引 2.1.2 修改表方式创建 2.1.3 创建表的时候指定索引 2.2 唯一索引 2.2.1 直接创建唯一索引 2.2.2 修改表方式创建 …...

Redis 教程系列之Redis PHP 使用 Redis(十二)
PHP 使用 Redis 安装 开始在 PHP 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 PHP redis 驱动,且你的机器上能正常使用 PHP。 接下来让我们安装 PHP redis 驱动:下载地址为:https://github.com/phpredis/phpredis/releases。 P…...

JavaScript语法和数据类型
基础 JavaScript 借鉴了 Java 的大部分语法,但同时也受到 Awk、Perl 和 Python 的影响。 JavaScript 是区分大小写的,并使用 Unicode 字符集。举个例子,可以将单词 Frh(在德语中意思是“早”)用作变量名。 var Frh …...

解决华为云服务器宝塔面板无法访问显示“此站点的连接不安全”问题
已经配置好安全组以及初始化宝塔面板,还是无法访问镜像管理页面,提示此站点的连接不安全。 解决方案 将地址https改为http即可进入。 成功登录后,开启面板SSL即可。...

【Python】 Python脚本实现某平台视频流下载
亲爱的玛丽 我会想念着你 我是多么的讨厌分离 加油站旁的海鸥 机场路上的松柏 挥挥手眼泪就落下来 我多想和那些光阴永远住下来 我不能 我不能 🎵 赵雷《玛丽》 在视频内容的分发上,m3u8格式的视频流越来越常见。它将视频切分成多个…...

LangChain核心模块 Model I/O——Prompts
Prompts 语言模型的提示是用户提供的一组指令或输入,用于指导模型的响应,帮助模型理解上下文并生成相关且连贯的基于语言的输出,例如回答问题、完成句子或参与某项活动。对话。 关键问题 如何在LLMs中使用少量示例(few-shot examples)—…...

关于Docker守护程序未运行导致的错误
01 在启动Docker之前,确保你已经安装了Docker并且Docker服务是运行的。以下是一些步骤可以帮助你解决这个问题: 首先,确保Docker已经正确安装在你的系统上。你可以通过运行以下命令来检查Docker是否已安装: docker --version如果…...