C++学习笔记(更新)-创新互联
本篇文章是个人的C++学习记录,也算是《C++ Primer Plus》的读书笔记,如有错误,期望指正
成都创新互联从2013年成立,先为吴堡等服务建站,吴堡等地企业,进行企业商务咨询服务。为吴堡企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。第一章 预备知识 类和对象在c++中,类是一种规范,描述了一种新型数据格式,对象是根据这种规范构造的特定数据结构
也就是说,类是抽象的,描述了有什么数据?对数据有哪些操作?而对象是具体的,是类的实例化
OOP(面向对象编程)并不仅仅是将数据和方法合并成类定义。
- 创建可重用的代码,这将减少大量的工作
- 信息隐藏可以保护数据,使其免遭不适当的访问
- 多态使得我们可以为运算符和函数创建多个定义,通过编程上下文来确定使用哪个定义
- 继承使得我们能够使用旧类派生出新类
模板是一种对类型进行参数化的工具,模板是泛型编程的基础,而泛型编程指的就是编写与类型无关的代码,是C++中一种常见的代码复用方式。模板分为模板函数和模板类;模板函数针对参数类型不同的函数;模板类主要针对数据成员和成员函数类型不同的类。
以上是对C++一些相较于C而言比较“新”的东西的总结,也是作者接下来要重点学习的地方
第二章 C++ 内置数据类型 基本类型整型变量:bool、char、signed char、unsigned char、short、 unsigned short、int、unsigned int、long、unsigned long
浮点型变量:float、double、long double
对于整型变量,头文件climits中定义了符号常量来表示类型的属性,可自行查看。
对于浮点类型,只要包含头文件“stdio.h”即可使用printf进行格式化输出。
C++算术运算符
+、-、*、/、%分别为加、减、乘、除、求余
对于除法,分别有int类型、long类型、double类型、float类型
#include#include"stdio.h"
using namespace std;
int main(){
int a=2,b=5;
float c=3.78343;
cout<
复合类型数组、C风格字符串、string类字符串、结构体、共用体、枚举、指针、vector类、array类……
数组声明数组,需要指出:储存在每个元素中的值的类型、数组名、数组中的元素数
typeName arrayName[arraySize];
//其中,arraySize指定元素数目,它必须是整型常数或const值,也可以是常量表达式。简而言之,不能是变量(程序运行过程赋值的量)
//数组是由其他类型来创建的复合类型,不能仅仅将某种东西声明为数组,它必须是特定类型的数组。
初始化数组,只有在定义数字时才能使用初始化,以后就不能使用了,也不能把一个数组赋给另一个数组,但可以用下标给元素赋值
int text[2] = {1,2}; float b[5] = {1,2,3};//可以不完全赋值,但不能超额
short things[] = {1,5,3,8};//计算元素个数时,可以用
int num = sizeof things / sizeof (short);
数组赋值
int main(){
int a[10];char b[10];string c[10];
cin>>a;//报错
cin>>b;//允许
cin>>c;//报错
return 0;//a=,b=,c=,也报错,不能整体赋值,只能单元素赋值
}//字符串(字符数组)可以整体cin,其他数组只能单元素cin
C风格字符串C风格字符串的本质是一个以空字符’\0’为结尾的char数组,将字符串存储到数组中,有两种方式
- 将数组初始化为字符串常量
char dog[8]={'f','a','t','e','s','s','a','\0'}
char bird[11]="Mr.Cheeps"//后两种方式会自动在结尾添加'\0'
char fish[]="Bubbles"//两种方式都要求数组长度大于等于字符串加'\0'的长度
//注意,不能先定义再赋值,如:char a[10];a="XXX";//a是数组第一个元素的指针,不能改变值。
//但是可以对单个元素赋值,如:char a[10];a[0]='x';
拼接字符串,在cout时,任何两个由空白(空格、制表符、换行符)分隔的字符串常量,都会自动拼接成一个。
cout<<"sfsagsg""sagsagasg";cout<<"sagasghsahas" "asfagasgsa";
cout<<"safsagasdhrwjnf"
"sagasghshdahna";
- 将键盘或文件输入读入到数组中
const int Arsize=20;
char name[Arsize];
char dessert[Arsize];
cin>>name;cin>>dessert;
cout<<"hello"<
面向行的输入
const int Arsize=20;
char name[Arsize];char dog[Arsize];
cin.getline(name,20);//遇见换行符或者到达大值(算上换行)停止,吃掉换行符,并把它变成空字符
cin.get(dog,20);//遇见换行符或者到达大值(算上换行)停止,不吃换行符,把它留在输入流中
cin.get();//接收留在输入流中的换行符
cin.get(name,Arsize).get();cin.get(dog,Arsize).get();//与cin.getline(name,Arsize).getline(dog,Arsize);等效
(cin>>name).get();//cin>>name返回的是cin对象,可以调用get()
string类#include#includeusing namespace std;
int main(){//string对象在很多方面与字符数组相同,初始化方式可以一样,可以cin,可以cout,也可以用数组表示法访问单个字符
string str1;string str2="asdfg";
str1="sdafasf";str2="asdfgg";
cout<
运算符重载- + 和 +=:连接字符串
- =:字符串赋值
- >、>=、< 和<=:字符串比较(例如a< b, aa< ab)
- ==、!=:比较字符串
- <<、>>:输出、输入字符串
注意:使用重载的运算符 + 时,必须保证前两个操作数至少有一个为 string 类型。例如,下面的写法是不合法的:
#include#includeint main()
{string str = "cat";
cout<< "apple" + "boy" + str; // illegal!
return 0;
}
查找string str;
cin >>str;
str.find("ab");//返回字符串 ab 在 str 的位置
str.find("ab", 2);//在 str[2]~str[n-1] 范围内查找并返回字符串 ab 在 str 的位置
str.rfind("ab", 2);//在 str[0]~str[2] 范围内查找并返回字符串 ab 在 str 的位置
//first 系列函数
str.find_first_of("apple");//返回 apple 中任何一个字符首次在 str 中出现的位置
str.find_first_of("apple", 2);//返回 apple 中任何一个字符首次在 str[2]~str[n-1] 范围中出现的位置
str.find_first_not_of("apple");//返回除 apple 以外的任何一个字符在 str 中首次出现的位置
str.find_first_not_of("apple", 2);//返回除 apple 以外的任何一个字符在 str[2]~str[n-1] 范围中首次出现的位置
//last 系列函数
str.find_last_of("apple");//返回 apple 中任何一个字符最后一次在 str 中出现的位置
str.find_last_of("apple", 2);//返回 apple 中任何一个字符最后一次在 str[0]~str[2] 范围中出现的位置
str.find_last_not_of("apple");//返回除 apple 以外的任何一个字符在 str 中最后一次出现的位置
str.find_last_not_of("apple", 2);//返回除 apple 以外的任何一个字符在 str[0]~str[2] 范围中最后一次出现的位置
//以上函数如果没有找到,均返回string::npos
cout<< string::npos;
子串str.substr(3); //返回 [3] 及以后的子串
str.substr(2, 4); //返回 str[2]~str[2+(4-1)] 子串(即从[2]开始4个字符组成的字符串)
替换str.replace(2, 4, "sz");//返回把 [2]~[2+(4-1)] 的内容替换为 "sz" 后的新字符串
str.replace(2, 4, "abcd", 3);//返回把 [2]~[2+(4-1)] 的内容替换为 "abcd" 的前3个字符后的新字符串
插入str.insert(2, "sz");//从 [2] 位置开始添加字符串 "sz",并返回形成的新字符串
str.insert(2, "abcd", 3);//从 [2] 位置开始添加字符串 "abcd" 的前 3 个字符,并返回形成的新字符串
str.insert(2, "abcd", 1, 3);//从 [2] 位置开始添加字符串 "abcd" 的前 [2]~[2+(3-1)] 个字符,并返回形成的新字符串
追加除了用重载的+
操作符,还可以使用函数来完成。
str.push_back('a');//在 str 末尾添加字符'a'
str.append("abc");//在 str 末尾添加字符串"abc"
删除str.erase(3);//删除 [3] 及以后的字符,并返回新字符串
str.erase(3, 5);//删除从 [3] 开始的 5 个字符,并返回新字符串
交换str1.swap(str2);//把 str1 与 str2 交换
其他str.size();//返回字符串长度
str.length();//返回字符串长度
str.empty();//检查 str 是否为空,为空返回 1,否则返回 0
str[n];//存取 str 第 n + 1 个字符
str.at(n);//存取 str 第 n + 1 个字符(如果溢出会抛出异常)
实例查找给定字符串并把相应子串替换为另一给定字符串
string 并没有提供这样的函数,所以我们自己来实现。由于给定字符串可能出现多次,所以需要用到find()
成员函数的第二个参数,每次查找之后,从找到位置往后继续搜索。直接看代码(这个函数返回替换的次数,如果返回值是 0 说明没有替换):
int str_replace(string &str, const string &src, const string &dest)
{int counter = 0;
string::size_type pos = 0;
while ((pos = str.find(src, pos)) != string::npos) {str.replace(pos, src.size(), dest);
++counter;
pos += dest.size();
}
return counter;
}
从给定字符串中删除一给定字串
方法和上面相似,内部使用erase()
完成。代码:
int str_erase(string &str, const string src)
{int counter = 0;
string::size_type pos = 0;
while ((pos = str.find(src, pos)) != string::npos) {str.erase(pos, src.size());
++counter;
}
return counter;
}
给定一字符串和一字符集,从字符串剔除字符集中的任意字符
int str_wash(string &str, const string src)
{int counter = 0;
string::size_type pos = 0;
while ((pos = str.find_first_of(src, pos)) != string::npos) {str.erase(pos, 1);
++counter;
}
return counter;
}
结构体数组可以将多个同类型元素存储在一起,而结构体可以将多种类型的数据存储在一起
struct text{
char name[20];
float volume;
double price;
};//结构体由其他类型数据组成,各类型依然遵守原来的用法,初始化方法、赋值方法……
int main(){//可以将一个结构赋给另一个结构(他们的类型相同时)
text a={};
//a={"asdad","2.5","2.5"};和大多数(除了string之外)混合类型一样,不能“赋值”
cin>>a.name;//可以单元素cin,不能整体cin(cin>>a)
text b={"sadfa",2.5,2.5};//初始化方式这样
//cout<共用体共用体可以存储不同的数据类型,但只能存储其中的一种类型
union one4all{
int int_val;
long long_val;
double double_val;
}pail;//可以用pail存储int、long、double中的一种,条件是在不同的时间进行。
共用体与结构体共用
struct wight{
char brand[20];int type;
union id{
long id_num;
char id_char[20];
}id_val;//id_val为组成部分,调用时要通过id_val,id_num,id_char为id_val成员,一个有值另一个就会没值
};
struct other{
char brand[20];int type;
union {
long id_num;
char id_char[20];
};//匿名共用体,id_num,id_char为结构体成员,一个有值另一个就会没值
};//匿名匿的是共用体和共用体变量的名,不单单是共用体变量的名
int main(){
wight a={};other b={};
cin>>a.id_val.id_char;
cin>>b.id_num;
return 0;//这里共用的作用是,可以根据type判断id是那种类型,这样就减少了空间占用。
}
枚举enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};
//让spectrum成为新类型的名称,它被称为枚举,花括号里的东西变成符号常量
spectrum a;//则a只能用花括号里的东西赋值
enum bits {one=1,two=2,three=3};
enum {on=1,tw=2,thre};//枚举相当于多个const,但是值只能是整数
指针和NEW
指针#includeusing namespace std;
int main(){
int a=3;int* b=&a;//类型名* 指针名=&变量名
cout<
int a=80;
const int* text0=&a;//指向 const int(常量)的指针
int* const test1=&a;//指向 int 的 const 的指针(指针本身不能被改变)
//注意,可以将const数据或非const数据的地址赋给指向const的指针(不能通过指向const的指针修改数据)
//但是不能将const数据的地址赋给非const指针
指针、数组、字符串int main(){
int num_text[5]={1,3,5,7,9};
char char_text[6]={'a','b','c','d','e','\0'};
int* nt=num_text;char* ct=char_text;
cout<
对于数组与字符串而言,指针名和数组名都是指向第一个元素的指针,但是cout字符串指针时,会打印出整个字符串
使用 *指针 等效于变量名, *(指针+1)意味着下一个元素,即text[3] == *(te+3)
new 与 deletetypeName * pointer_name = new typeName
//程序员告诉new要一个什么类型的内存,new就会找到这样一个内存块,并返回它的指针
#includeusing namespace std;
int main(){
int n;cin>>n;
int* int_point = new int;
int* ints_point = new int[n];
char* char_point = new char;
char* chars_point = new char[n];//new可以用来创建动态数组
delete [] chars_point;
delete [] ints_point;
delete int_point;
delete char_point;
}//new完不要忘记delete
使用new创建动态结构
struct test{
int num;
char name;
};
int main(){
auto* st = new test;
cin>>st->name;
cin>>(*st).num;
cout<<(*st).name<<" "<num;
return 0;
}
d 3
d 3
int main(){
auto* st = new test[3];
cin>>(st+1)->name;
cin>>(*(st+1)).num;
cout<<(*(st+1)).name<<" "<<(st+1)->num;
return 0;
}
d 3
d 3
数组的替代品
模板类 vectorhttps://www.runoob.com/w3cnote/cpp-vector-container-analysis.html
引用变量(见函数探幽部分)
第三章 循环与关系表达式
for 循环
while循环
do…while循环
第四章 函数C++自带了一个包含函数的大型库(标准ANSI库加上多个C++类),但真正的编程乐趣在于编写自己的函数
构建函数- 提供函数定义
void functionName(parameterList)//参数列表,要说清楚参数类型和参数名
{
wuliwala;
return xxx; //可选
}
typeName functionName(parameterList)
{
wuliwala;
return xxx; //必须和typeName一样
}
- 提供函数原型
void functionName(parameterList);//参数列表可以只写参数类型
typeName functionName(parameterList);//在使用函数之前声明(一般放在main()之前)
- 调用函数
functionName(实参(用逗号隔开));
注意C++对于返回值的类型有一定的限制:不能是数组,但可以是其他任何类型(整数、浮点数、指针、甚至可以是结构和对象)
但是,C++可以将数组作为结构或对象的组成部分来返回。另外,C++可以以任何数据类型为参数
void test0(int a){//以变量为参数,函数体内用的是实参的副本,副本改变不会影响实参
cin>>a;
cout<>*a;//以指针为参数,通过指针会影响实参
cout<<*a<<"\n";
}
void test2(const int* a){
//"cin>>*a;"错误,不能通过const的指针修改指向值
cout<<*a<<"\n";
}
//const可以保护指针指向的内容,确保它不会在函数里被修改,数组的指针也可以保护,啥都能保护
int main(){
int b;
int* c=&b;
test1(c);
cout<
int test0(int* a){
cin>>a[0];
cin>>*(a+1);
return a[0]+1;
}
//指针会被函数当做数组名直接用
int test1(int a[]){
cin>>a[0];
cin>>*(a+1);
return a[0]+1;
}
//如果实际想操作的不是数组,建议不要用指针,直接用int test(int a){}比较好
int main(){
int a[3]={1,2,3};
cout<
int a=80;
const int* text0=&a;//指向 const int(常量)的指针
int* const test1=&a;//指向 int 的 const 的指针(指针本身不能被改变)
//注意,可以将const数据或非const数据的地址赋给指向const的指针(不能通过指向const的指针修改数据)
//但是不能将const数据的地址赋给非const指针
return 0;
使用数组区间的函数/函数与字符串int sum_arr(const int* begin,const int* end){
const int* pt;
int total=0;
for(pt=begin;pt!=end;pt++){
total = total + *pt;
}
return total;
}
int main(){
int a[5]={1,2,3,4,5};
cout<
char* builder(char c,int n){
char* pstr = new char[n+1];
pstr[n]='\0';
while (n-->0){
pstr[n]='c';
}
return pstr;
}
int main(){
char* ps=builder('d',3);
//delete[] pstr;不能解放pstr,它在函数里面,应该用ps把它接出来,然后解放
cout<
函数与结构struct time{
int hours;
int mins;
};
time sum(time m,time n){
time total={0,0};
total.hours=m.hours+n.hours;
total.mins=m.mins+n.mins;
return total;
}
int main(){
time m = {3,2};
time n = {4,6};
time s = sum(m,n);
cout<
函数与二维数组int sum(int ar2[][4],int size){
int total=0;
for(int r=0;r{1,2,3,4},{9,8,7,6},{2,4,6,8}};
//data是一个数组名,该数组有三个元素,每个元素都是由4个int组成的数组
//也就是说,每个元素都是一个指针,指向由4个int组成的数组的指针
//以data为参数时,应该这样描述:int (*ar2)[4] 或者 int ar2[][4]
//这时,我们要对行数用另一个参数加以限制
int sums=sum(data,2);
cout<
函数与stringvoid display(const string sa[],int n){
for(int i=0;i
递归
含义第一要素:明确函数想要干什么
对于递归,我觉得很重要的一个事就是,这个函数的功能是什么,他要完成什么样的一件事,而这个,是完全由你自己来定义的。也就是说,我们先不管函数里面的代码什么,而是要先明白,你这个函数是要用来干什么。
例如,我定义了一个函数
// 算 n 的阶乘(假设n不为0)
int f(int n){
}
这个函数的功能是算 n 的阶乘。好了,我们已经定义了一个函数,并且定义了它的功能是什么,接下来我们看第二要素。
第二要素:寻找递归结束条件
所谓递归,就是会在函数内部代码中,调用这个函数本身,所以,我们必须要找出递归的结束条件,不然的话,会一直调用自己,进入无底洞。也就是说,我们需要找出当参数为啥时,递归结束,之后直接把结果返回,请注意,这个时候我们必须能根据这个参数的值,能够直接知道函数的结果是什么。
例如,上面那个例子,当 n = 1 时,那你应该能够直接知道 f(n) 是啥吧?此时,f(1) = 1。完善我们函数内部的代码,把第二要素加进代码里面,如下
// 算 n 的阶乘(假设n不为0)
int f(int n){
if(n == 1){
return 1;
}
}
有人可能会说,当 n = 2 时,那我们可以直接知道 f(n) 等于多少啊,那我可以把 n = 2 作为递归的结束条件吗?
当然可以,只要你觉得参数是什么时,你能够直接知道函数的结果,那么你就可以把这个参数作为结束的条件,所以下面这段代码也是可以的。
// 算 n 的阶乘(假设n>=2)
int f(int n){
if(n == 2){
return 2;
}
}
注意我代码里面写的注释,假设 n >= 2,因为如果 n = 1时,会被漏掉,当 n<= 2时,f(n) = n,所以为了更加严谨,我们可以写成这样:
// 算 n 的阶乘(假设n不为0)
int f(int n){
if(n<= 2){
return n;
}
}
第三要素:找出函数的等价关系式
第三要素就是,我们要不断缩小参数的范围,缩小之后,我们可以通过一些辅助的变量或者操作,使原函数的结果不变。
例如,f(n) 这个范围比较大,我们可以让 f(n) = n * f(n-1)。这样,范围就由 n 变成了 n-1 了,范围变小了,并且为了原函数f(n) 不变,我们需要让 f(n-1) 乘以 n。
说白了,就是要找到原函数的一个等价关系式,f(n) 的等价关系式为 n * f(n-1),即
f(n) = n * f(n-1)。
找出了这个等价,继续完善我们的代码,我们把这个等价式写进函数里。如下:
// 算 n 的阶乘(假设n不为0)
int f(int n){
if(n<= 2){
return n;
}
// 把 f(n) 的等价操作写进去
return f(n-1) * n;
}
至此,递归三要素已经都写进代码里了,所以这个 f(n) 功能的内部代码我们已经写好了。
例子//斐波那契数列的是这样一个数列:1、1、2、3、5、8、13、21、34....,
// 即第一项 f(1) = 1,第二项 f(2) = 1.....,第 n 项目为 f(n) = f(n-1) + f(n-2)。
// 求第 n 项的值是多少。
int fibo(int n){
if(n<=2){
return 1;
}
return fibo(n-1)+fibo(n-2);
}
int main(){
cout<
//一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
int gua(int n){
if(n<=2){
return n;
}
return gua(n-1)+gua(n-2);//第一次跳一级还剩n-1级,跳两级还剩n-2级
}
int main(){
cout<
函数指针#includedouble betsy(int);
double pam(int);
void estimate(int lines, double (*pf)(int));//传参时特征标和返回值都应匹配
// 第二个参数是一个函数指针,它指向参数为int、返回值为double的指针
int main()
{
using namespace std;
int code;
cout<< "How many lines of code do you need? ";
cin >>code;
cout<< "Here's Betsy's estimate:\n";
estimate(code, betsy);
cout<< "Here's Pam's estimate:\n";
estimate(code, pam);
// cin.get();
// cin.get();
return 0;
}//函数指针使得一个函数可以调用不同的函数
//直接在函数中调用函数则做不到这一点
double betsy(int lns)
{
return 0.05 * lns;
}//两种函数对应两种“算法”
//使用函数指针可以灵活的任意调用
double pam(int lns)
{
return 0.03 * lns + 0.0004 * lns * lns;
}
//函数名就是函数指针,声明指针的格式:typeName (*pointName)(argumentType)
void estimate(int lines, double (*pf)(int))
{
using namespace std;
cout<< lines<< " lines will take ";
cout<< (*pf)(lines)<< " hour(s)\n";
}//(*pf)可以是任意满足类型的函数,而不是确定的函数
How many lines of code do you need?30
Here's Betsy's estimate:
30 lines will take 1.5 hour(s)
Here's Pam's estimate:
30 lines will take 1.26 hour(s)
const double* f1(const double ar[],int n);
const double* f2(const double [],int);//返回值为指向常量的指针,参数列表为常量数组和int
const double* f3(const double *,int);
//需要注意的是,f1、f2、f3的特征标看似不同,但实际上相同
//接下来声明一个指针,它可指向这三个函数之一,假设给指针名为pa
const double* (*pl)(const double*,int);
const double* (*pl)(const double*,int) = f1;//可以在声明的同时初始化
auto p2 = f2;//用auto将更加方便
typedefint main(){
typedef double real;
real a=3.5;
typedef const double* (*p_fun)(const double*,int);
//*p_fun = f1错误用法
p_fun f1;//正确用法
typedef int* test;
test b;//正确用法
//*test b;错误用法
}
第五章 函数探幽
内联函数常规函数和内联函数之间的主要区别不在于便携方式,而在于C++编译器如何将他们组合到程序中
用通俗的语言来讲,普通函数在程序调用函数时,会跳到函数内存块,再跳回来,
而对于内联函数,编译器将使用相应的函数代码替换函数调用,
内联函数的运行速度比常规函数稍快,但需要占用更多内存。
#includeusing namespace std;
//使用inline函数,必须在函数声明和函数定义前加上关键字inline
inline double square(double x)
{
return x * x;
}//这里直接在main之前定义,就不用再声明了
int main()
{
double a, b;
double c = 13.0;
a = square(5.0);
b = square(4.5 + 7.5); //可以用表达式做参数
cout<< "a = "<< a<< ", b = "<< b<< "\n";
cout<< "c = "<< c;
cout<< ", c squared = "<< square(c++)<< "\n";
cout<< "Now c = "<< c<< "\n";
return 0;
}
a = 25, b = 144
c = 13, c squared = 169
Now c = 14
引用变量C++新增了一种复合类型——引用变量,引用是已定义的变量的别名,它最主要的用途是用作函数的形参,
通过将引用变量作为参数,函数将使用原始数据,而不是其副本。
//C和C++使用&来指示变量的地址,C++给&符号赋予了另一个含义
int rats;
int& rodents = rats;//使rodents成为rats的另一个名字
//在上句中,&不是地址运算符,而是类型标识符的一部分
//就像char*表示指向char的指针一样,int&是指向int的引用
//上述引用声明允许将rats和rodents互换,它们指向相同的值和内存单元
int rats = 101;
int& rodents = rats;
int* prats = &rats;
//引用看上去很像(*指针),但实际上,引用还是不同于指针的,除了表示方法不同外,还有其他差别
//比如,引用不能像指针那样,先声明、再赋值,必须在声明引用变量时同时初始化
//引用更接近const指针,必须在创建时初始化,一旦与某个变量关联起来,就将一直效忠于它。
int& rodents = rats;
int* const pr = &rats;
//二者类似,其中引用rodents扮演的角色与表达式*pr相同
//引用变量的值一旦确定,就不会更改
int rats = 101;
int* pt = &rats;
int& rodents = *pt;//rodents在此处初始化
int bunnies = 50;
pt = &bunnies;//将pt改变,而rodents似乎会随着pt改变(毕竟初始化的时候,rodents是*pt的别名)
//但是,rodents并不会被改变,因为它效忠的永远是那一刻的*pt,也就是rats
//哪怕*pt在未来变了,它也不会改变
将引用作为函数参数引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名
这种做法并没有绕过引用不能先定义再赋值的设定,因为函数直到被调用,它里面的变量才会被定义
void swapr(int& a, int& b)
{
int temp;
temp = a; a = b; b = temp;
}
void swapp(int* p, int* q)
{
int temp;
temp = *p; *p = *q; *q = temp;
}
void swapv(int a, int b)
{
int temp;
temp = a; a = b; b = temp;
}
int main()
{
using namespace std;
int wallet1 = 300; int wallet2 = 350;
cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;
cout<< "Using references to swap contents:\n";
swapr(wallet1, wallet2);//参数传递实现了int& a=wallet1 和 int& b=wallet2
cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;
cout<< "Using pointers to swap contents again:\n";
swapp(&wallet1, &wallet2);//参数传递实现了int* a=&wallet1 和 int* b=&wallet2
cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;
cout<< "Trying to use passing by value:\n";
swapv(wallet1, wallet2);//参数传递实现了int a=wallet1 和 int b=wallet2
cout<< "wallet1 = $"<< wallet1<< " wallet2 = $"<< wallet2<< endl;
return 0;
}
wallet1 = $300 wallet2 = $350
Using references to swap contents:
wallet1 = $350 wallet2 = $300//使用引用互换,成功
Using pointers to swap contents again:
wallet1 = $300 wallet2 = $350//使用指针互换,成功(又换回来了)
Trying to use passing by value:
wallet1 = $300 wallet2 = $350//使用副本互换(值传递),失败(没变)
注意事项避免返回函数终止时不再存在的内存单元引用
const int& clone(int& ft)//本代码块为错误示范
{
int a;
a=ft;
return a;//把ft的值给了a,并且返回a
}//该函数返回了一个临时变量的引用
const string& version3(string& s1,const string& s2)
{
string temp;
temp = s2+s1+s2;
return temp;//返回了临时变量的引用
}
string version1(const string & s1, const string & s2)
{
string temp;
temp = s2 + s1 + s2;
return temp;//返回类型为string,temp将被复制到一个临时存储单元
}
const string & version2(string & s1, const string & s2) // has side effect
{
s1 = s2 + s1 + s2;
return s1; //返回了已有的引用
}
const string & version3(string & s1, const string & s2) // bad design
{
string temp;
temp = s2 + s1 + s2;
return temp;//返回了临时变量的引用,错误示范
}
int main()
{
string input;
string copy;
string result;
cout<< "Enter a string: ";
getline(cin, input);
copy = input;
cout<< "Your string as entered: "<< input<< endl;
result = version1(input, "***");
cout<< "Your string enhanced: "<< result<< endl;
cout<< "Your original string: "<< input<< endl;
result = version2(input, "###");
cout<< "Your string enhanced: "<< result<< endl;
cout<< "Your original string: "<< input<< endl;
cout<< "Resetting original string.\n";
input = copy;
result = version3(input, "@@@");
cout<< "Your string enhanced: "<< result<< endl;
cout<< "Your original string: "<< input<< endl;
return 0;
}
Enter a string:sdfaf
Your string as entered: sdfaf
Your string enhanced: ***sdfaf***
Your original string: sdfaf
Your string enhanced: ###sdfaf###
Your original string: ###sdfaf###//version2直接用了input的引用,导致input也随之变化
Resetting original string.
Process finished with exit code -1073741819 (0xC0000005)//version3报错
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
当前标题:C++学习笔记(更新)-创新互联
分享网址:http://cdiso.cn/article/cdidjo.html