首页 > 代码库 > 引用&&指针&&返回值&&定义声明&&生命周期&&const

引用&&指针&&返回值&&定义声明&&生命周期&&const

1. 首先了解一下声明和定义的区别:

?    声明,其实就是描述一个元素是有什么构成的;?    定义,其实就是在内存中划分出一个区域且用符号关联起来;?    变量和对象不加extern永远是定义,类中的除外。 函数只有函数头是声明,有函数体的是定义。 类永远只是声明。类成员函数的函数体是定义?    函数的声明和定义的不同点在于函数的声明不包含函数体,仅仅是fact();

2. 其次了解一下局部对象的生命周期

?    局部变量i.    形参和函数体内定义的变量统称局部变量ii.    局部变量转换成为全局变量,则使用static关键字就能实现;iii.    局部变量的生存周期,在函数调用时被创建,在函数结束的时候就销毁,例:形参iv.    有两种方式可以将函数体内值得改变传回主函数,一种是将形参设置为引用的形参,其实是实参和形参绑定在一块儿,因而值可以回传,另外一种是通过指针形参,改变实参传递过来地址的值,去改变函数体内的值。

3. 引用

A.    可能出现的错误情况:Int  ival =1024;Int  &refVal;  //引用必须进行初始化,改正: int &refVal = ival;//因为无法令引用重新绑定到另外一个对象,因此引用必须进行初始化;引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字;B.    情况2:Int  &refVal1 =10; // 错误:引用类型的初始值必须是一个对象,不能是字面值;double dval = 3.14;int &refVal =dval; //错误,此处引用类型的初始值必须是int类型的对象;C.    总结: 引用初始值必须是一个对象,不能是字面值;引用必须进行初始化;引用类型和绑定的类型必须一致。切记,哪怕类型之间可以相互转换,也不可以,必须类型一致;

4. const &

A.    const 用法i.    定义一种变量,它的值不能被改变。ii.    好处:用一个变量来表示缓冲区的大小,使用变量的好处是当我们觉得缓冲区大小不再合适时,很容易对其进行调整,但是我们又要随时警惕防止程序一不小心改变这个值。因而用constiii.    const 必须进行初始化;iv.    引用的好处:避免拷贝,可以直接比较;使用引用形参返回额外信息,主要是为了改善一个函数只能返回一个值得情况,这样函数可以返回多值。v.    补充:顶层const是指指针本身是个常量,指针所指的内容是可以改变的,当形参有顶层const时,顶层const可以忽略掉,传给他常量对象或者非常量对象都是可以的,因为传入形参的本身就是局部变量,不会改变实参的值。vi.    数组引用形参B.    总结:常量引用也必须进行初始化,并且不能通过该对象别名去修改已知非常量的值,绑定的值可以是常量也可以是非量,和引用有一点不同double dval = 3.14; const int &ri =dval;//当类型不同时,转换时可以进行的.
//常量引用的好处之一避免拷贝对象~
bool
isShorter(const string &s1, const string &s2){return s1.size() < s2.size();}//避免拷贝对象

//常量引用的好处之二___可以使函数返回多个值

//返回s中c第一次出现的位置索引//引用形参occurs负责统计c出现的总次数string::size_type find_char(const string &s, char c, string::size_type &occurs){auto ret = s.size(); //第一次出现的位置occurs = 0;for (decltype(ret) i = 0; i != s.size(); ++i){if (s[i] == c){if (ret == s.size())ret = i; //记录c第一次出现的位置++occurs;//将出现的次数+1}}return ret; //出现次数通过occurs隐式传回}int main(){string s = { "Hello world, welcome to beijing !" };decltype(s.size()) ctr = 0;auto index = find_char(s, o, ctr);cout << "string S has " << ctr << " times o" << endl;}

5. 返回值为&

A.    关于返回值是如何返回的:返回的值用于初始化调用点的一个临时量,该临时两就是函数调用的结果。B.    不要返回局部对象的引用或指针,局部对象会在函数调用结束的时候释放内存;C.    引用返回的是左值;

//返回值为常饮用出现的错误之一

const string &manip(){string ret;//以某种方式改变一下retif (!ret.empty())return ret; //错误:返回的是对局部变量的引用elsereturn "Empty"; //错误:"Empty"是一个局部临时量}//  有的返回值也是局部变量,但是不会提示错误~~~1>e:\c程序\finalconst\finalconst\finalconst.cpp(10): warning C4172: 返回局部变量或临时变量的地址1>e:\c程序\finalconst\finalconst\finalconst.cpp(12): warning C4172: 返回局部变量或临时变量的地址

6. 数组形参

A.    数组的性质:不允许拷贝数组,使用数组时通常会将其转换成指针;因而函数传递数组时,实际上传递的是指向数组首元素的指针。B.    首先需要看一下三种等价的形参是数组的形式void  print(const int*);void print (const int[]);//可以看出来,函数的意图是作用于一个数组//因而 void print(const int *arr[])==void print (const int **arr)void print (const int[10]); //这里的维度表示我们期望数组含有多少元素实际不一定这里每个函数的唯一形参都是const int * 类型,这是一个底端const

exe1:当形参为内置类型,不是混合类型(如指针、引用)

void reset(int ival){ival = 3;cout << ival << endl;}int main(){int ival1 = 5;reset(ival1);cout << ival1 << endl;}//输出值为3,5

exe2:当形参为指针类型的时候

void reset(int *ival){*ival = 3;cout << *ival <<" "<< ival << endl;//当传递的值为指针的时候,可以在函数体内//通过指针改变指对象的值,并且该值被传回主函数,间接地改变主函数中所指地址中的值;int ival2 = 4;ival = &ival2;//改变函数传入的实参的值,也就是改变传入地址的值,仅仅是局部变量的拷贝,//并不能使实参的值改变,如果注释掉*ival = 3;该函数调用不会改变主函数的任意值}int main(){int ival1 = 5;reset(&ival1);cout << ival1 <<" "<<&ival1<< endl;}输出值为:3,002AFB04        3,002AFB04

exe:当形参为引用类型的时候

void reset(int &ival){ival = 3;cout << ival << " " << &ival<< endl;}int main(){int ival1 = 5;reset(ival1);cout << ival1 << " " << &ival1 << endl;}输出值为:3,002AFB48         3,002AFB48

exe:当形参为内置类型时,在函数体内定义的变量是局部变量,但是仍然可以将值赋给主函数中的某个变量~~,这是值传递~~

int fact(int val){int ret = 1;while (val > 1)ret *= val--;return ret;}int main(){int j = fact(5);cout << "5! is " << j << endl;return 0;}

exe:当形参是未知个数,但是类型相同的时候,可以用类initializer_list

void error_msg(initializer_list<string> i1){for (auto beg = i1.begin(); beg != i1.end(); ++beg)cout << *beg << " ";cout << endl;}int main(){ string expected;string actual;if (expected != actual)error_msg({ "functional", expected, actual });elseerror_msg({ "functional", "okay" });return 0;}

 

最后当指针出现的各种情况的总结:

 1 #include <iostream> 2 #include <string> 3 #include<vector> 4 using namespace std; 5  6  7 int g_val = 8; 8 //数组的数组 9 void  pointer(int *ival)10 {11     *ival = 10;12     cout << *ival << " " << ival << endl;13     //在此处应该是把实参的值改为10,并且地址应该相同14 }15 void  pointer1(const int *ival)16 {17     cout << *(ival + 2) << endl;    18     //输入的是数组的首地址,该地址的值不能改变,但是指针能向下移动19 }20 void pointer2(int *arr[])21 {22     cout <<**arr<<" "<< *arr << " " << arr << endl;23 }24 void pointer3(int(*arr)[4])//等价于void pointer3(int(arr[])[4])25 {26     for (size_t i = 0; i < 4; i++)27     {28         cout << (*arr)[i] <<" "<<arr[i]<< endl;//对指针数组的使用~29     }30 }31 int *pointer4()//有意义的为int * const pointer4()32 {33     34     return &g_val;//可以在类的使用中返回该值的地址,可以用于修改该对象的值;35 }36  37 38 int main()39 {40     int val = 4;41     int val1[4] = { 0, 1, 2, 3 };42     int val2[2][4] = { 0, 1, 2, 3, 4, 5, 6, 7 };43     int *val3 = &val;44     //多维数组初始化-----45     //还可以初始化{{0,1,2},{3,4,5}}46 47     //test void  pointer(int *ival)48     pointer(&val);49     cout << val << " " << &val << endl;50     pointer(val1);51     cout << val1[0] << " " << val1 << endl;52     //test void  pointer1(const int *ival)53     pointer1(val1);54 55     //test void pointer2(int **arr)56     pointer2(&val3);57     //void pointer3(int(*arr)[4])58     pointer3((val2+1));59     //test int  *pointer4()60     int *val4=pointer4();61     cout << val4 << " " << *val4 << endl;62     *val4 = 90;63     cout << val4 << " " << *val4 << endl;64     return 0;65 }

实验结果:

 

 

参考:

  函数返回值是否使用引用类型的问题:理解引用、返回值
  1. http://blog.csdn.net/sxhelijian/article/details/7466540  

      理解一般指针类型

  1. http://www.cnblogs.com/dzry/archive/2011/05/12/2044835.html 

 

引用&&指针&&返回值&&定义声明&&生命周期&&const