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. 好处:用一个变量来表示缓冲区的大小,使用变量的好处是当我们觉得缓冲区大小不再合适时,很容易对其进行调整,但是我们又要随时警惕防止程序一不小心改变这个值。因而用const iii. 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;//以某种方式改变一下ret if (!ret.empty()) return ret; //错误:返回的是对局部变量的引用 else return "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 }); else error_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 }
实验结果:
参考:
函数返回值是否使用引用类型的问题:理解引用、返回值
- http://blog.csdn.net/sxhelijian/article/details/7466540
理解一般指针类型
- http://www.cnblogs.com/dzry/archive/2011/05/12/2044835.html
时间: 2024-10-17 17:13:50