c++基本使用(引用)
概念
引用是已定义的变量的别名。
引用的主要用途是用作函数的形参和返回值。
声明/创建引用的语法:
数据类型 &引用名=原变量名;
引用的数据类型要与原变量名的数据类型相同。
引用名和原变量名可以互换,它们值和内存单元是相同的。
必须在声明引用的时候初始化,初始化后不可改变。
C和C++用&符号来指示/取变量的地址,C++给&符号赋予了另一种含义。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
int main()
{
// 声明 / 创建引用的语法:数据类型 & 引用名 = 原变量名;
int a = 3; // 声明普通的整型变量。
int& ra = a; // 创建引用ra,ra是a的别名。
cout << " a的地址是:" << &a <<"\t"<< "a的值是:" << a << endl;
cout << " ra的地址是:" << &ra <<"\t"<< "ra的值是:" << ra << endl;
ra = 5;
cout << " a的地址是:" << &a << "\t"<<"a的值是:" << a << endl;
cout << " ra的地址是:" << &ra << "\t"<<"ra的值是:" << ra << endl;
}
引用的本质
引用是指针常量的伪装。(引用和指针本质上没有区别)
引用是编译器提供的一个有用且安全的工具,去除了指针的一些缺点,禁止了部分不安全的操作。
程序员拥有引用,但编译器仅拥有指针(地址)。
引用的底层机制实际上是和指针一样的,编译器还是会把引用解释为指针。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
int main()
{
// 声明 / 创建引用的语法:数据类型 & 引用名 = 原变量名;
// 语法:数据类型 * const 变量名;
int a = 3; // 声明普通的整型变量。
int& ra = a; // 创建引用ra,ra是a的别名。 把int&替换成int* const 把a替换成&a
int* const rb = &a; // 声明指针常量rb,让它指向变量a。
cout << " a的地址是:" << &a << " a的值是:" << a << endl;
cout << " ra的地址是:" << &ra << " ra的值是:" << ra << endl; // 把&ra替换成ra,把ra替换成*ra
cout << " rb的值是 :" << rb << " *rb的值是:" << *rb << endl;
ra = 5;
cout << " a的地址是:" << &a << " a的值是:" << a << endl;
cout << " ra的地址是:" << &ra << " ra的值是:" << ra << endl;
cout << " rb的值是 :" << rb << " *rb的值是:" << *rb << endl;
}
引用使用于函数的参数
按引用传递:把函数的形参声明为引用,调用函数的时候,形参将成为实参的别名。
(传值、传地址、传引用只是说法不同,其实都是传值)
引用的本质是指针,传递的是变量的地址,在函数中,修改形参会影响实参。
传递值、传递地址、传递引用的区别:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void func1(int no, string str) // 传值。
{
no = 8;
str = "我有一只小小鸟。";
cout << "亲爱的" << no << "号:" << str << endl;
}
void func2(int* no, string* str) // 传地址。
{
*no = 88;
*str = "我有一只小小鸟。";
cout << "亲爱的" << *no << "号:" << *str << endl;
}
void func3(int &no, string &str) // 传引用。
{
no = 888;
str = "我有一只小小小小鸟。";
cout << "亲爱的" << no << "号:" << str << endl;
}
int main()
{
int bh = 3; // 超女的编号。
string message = "我是一只傻傻鸟。"; // 向超女表白的内容。
func1(bh, message); // 传值。
cout << "亲爱的" << bh << "号:" << message << endl;
cout<<endl;
func2(&bh, &message); // 传地址。
cout << "亲爱的" << bh << "号:" << message << endl;
cout<<endl;
func3(bh, message); // 传引用。
cout << "亲爱的" << bh << "号:" << message << endl;
cout<<endl;
}
对于结构体,传递值、传递地址、传递引用的区别:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
struct st_girl // 定义超女结构体。
{
int no; // 超女编号。
string str; // 表白内容。
};
void func1(st_girl girl) // 传值。
{
girl.no = 8;
girl.str = "我有一只小小鸟。";
cout << "亲爱的" << girl.no << "号:" << girl.str << endl;
}
void func2(st_girl *girl) // 传地址。
{
girl->no = 88;
girl->str = "我有一只小小鸟。";
cout << "亲爱的" << girl->no << "号:" << girl->str << endl;
}
void func3(st_girl & girl) // 传引用。
{
girl.no = 888;
girl.str = "我有一只小小小小鸟。";
cout << "亲爱的" << girl.no << "号:" << girl.str << endl;
}
int main()
{
st_girl girl = { 3,"我是一只傻傻鸟。" };
func1(girl); // 传值。
cout << "亲爱的" << girl.no << "号:" << girl.str << endl;
cout<<endl;
func2(&girl); // 传地址。
cout << "亲爱的" << girl.no << "号:" << girl.str << endl;
cout<<endl;
func3(girl); // 传引用。
cout << "亲爱的" << girl.no << "号:" << girl.str << endl;
cout<<endl;
}
传递一级指针,在函数中动态开辟内存空间:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void func1(int** p) // 传地址,实参是指针的地址,形参是二级指针。
{
*p = new int(3); // p是二级指针,存放指针的地址。
cout << "func1内存的地址是:" << *p << ",内存中的值是:" << **p << endl;
}
void func2(int*& p) // 传引用,实参是指针,形参是指针的别名。
{
p = new int(3); // p是指针的别名。
cout << "func2内存的地址是:" << p << ",内存中的值是:" << *p << endl;
}
int main()
{
int* p = nullptr; // 存放在子函数中动态分配内存的地址。
func1(&p); // 传地址,实参填指针p的地址。
cout << "main 内存的地址是:" << p << ",内存中的值是:" << *p << endl;
cout<<endl;
func2(p); // 传引用,实参填指针p。
cout << "main 内存的地址是:" << p << ",内存中的值是:" << *p << endl;
cout<<endl;
delete p;
}
引用的形参和const
将引用形参声明为const的理由有三个:
使用const可以避免无意中修改数据的编程错误。
使用const使函数能够处理const和非const实参,否则将只能接受非const实参。
使用const,函数能正确生成并使用临时变量。
//如果引用的数据对象类型不匹配,当引用为const时,C++将创建临时变量,让引用指向临时变量。
const int& a = 8;
//等效于下面的代码
int tem = 8;
const int& p = tem;
在形参是指针变量或引用的时候;不能传值,只能传递地址或变量名
为了解决这个问题,就在引用的前面加上const (但指针还是不能传递值)
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void func1(int no, string str) // 传值。
{
cout << "亲爱的" << no << "号:" << str << endl;
}
void func2(const int* no,const string* str) // 传地址。
{
cout << "亲爱的" << *no << "号:" << *str << endl;
}
void func3(const int& no, const string& str) // 传引用。
{
cout << "亲爱的" << no << "号:" << str << endl;
}
int main()
{
int bh = 3; // 超女的编号。
string message = "我是一只傻傻鸟。"; // 向超女表白的内容。
func1(bh, message); // 传值。
func2(&bh, &message); // 传地址。
func3(bh, message); // 传引用。
cout<<"不声明普通变量;直接传递值,调用函数"<<endl;
func1(8, "我是一只小小鸟。");
//func2(8, "我是一只小小鸟。"); //不能传递数字
func3(80, "我是一只小小鸟。"); //可以传递数字
//如果引用的数据对象类型不匹配,当引用为const时,C++将创建临时变量,让引用指向临时变量。
const int& a = 8;
//等效于下面的代码
int tem = 8;
const int& p = tem;
}
引用用于函数的返回值
传统的函数返回机制与值传递类似。
函数的返回值被拷贝到一个临时位置(寄存器或栈),然后调用者程序再使用这个值。
问题:占用内存空间。。
解决方案:返回地址或者返回引用不会拷贝内存。
语法:返回值的数据类型& 函数名(形参列表);
注意:
如果返回局部变量的引用,其本质是野指针,后果不可预知。
可以返回函数的引用形参、类的成员、全局变量、静态变量。
返回引用的函数是被引用的变量的别名,将const用于引用的返回类型。
被引用变量的生命周期一定得比引用长
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
int func1(){
return 1;
}
int& func3() // 返回的是引用。
{
//局部变量的引用,其本质是野指针
int ii = 3;
return ii;
}
int& func31() // 返回的是引用。
{
//引用静态变量
static int ii = 3;
return ii;
}
//引用全局变量
int iii = 3;
int& func32() // 返回的是引用。
{
return iii;
}
int& func33() // 返回的是引用。
{
int ii = 3;
return ii;
}
int& func2(int &ra) // 返回的是引用。
{
ra++;
cout << "ra的地址是:" << &ra << ",ra=" << ra << endl;
return ra;
}
const int& func22(int &ra) // 返回的是引用。
{
ra++;
cout << "ra的地址是:" << &ra << ",ra=" << ra << endl;
return ra;
}
int main()
{
cout<<"返回值"<<endl;
cout<<func1()<<endl;
//返回局部变量的引用,其本质是野指针(局部变量被释放掉了)
cout<<"返回局部变量的引用"<<endl;
int& b1 = func3();
cout<<"局部变量被释放,主函数中的引用变成了野指针\t";
cout<<b1<<endl;
cout<<"返回静态变量的引用"<<endl;
int& b31 = func31();
cout<<b31<<endl;
cout<<"返回静态变量的引用"<<endl;
int& b32 = func32();
cout<<b32<<endl;
cout<<"函数的引用形参"<<endl;
int a = 3;
int& b = func2(a); // 返回的是引用。
cout << " a的地址是:" << &a << ", a=" << a << endl;
cout << " b的地址是:" << &b << ", b=" << b << endl;
cout<<"可以看出,传递的变量,函数的参数,返回值都是指向同一个变量"<<endl;
cout<<"返回引用的函数是被引用的变量的别名,因此,func2(a) = 10等价于:a = 10"<<endl;
func2(a) = 10; // 返回引有的函数是被引用的变量的别名。
cout << " a的地址是:" << &a << ", a=" << a << endl;
cout << " b的地址是:" << &b << ", b=" << b << endl;
cout<<"返回引用的函数是被引用的变量的别名,如不不希望返回值被当成变量来使用,就在返回值前面加上const关键字."<<endl;
//func22(a) = 100; //不能当变量用了,所以报错;
}