C++对C语言的扩展(1)--引用

C++对C语言的扩展--引用

  • 1 引用
    • 1.1 变量名
    • 1.2 引用的概念
    • 1.3 规则
    • 1.4 引用作为函数参数
    • 1.5 引用的意义
    • 1.6 引用的本质
    • 1.7 引用作为函数的返回值
      • 1.7.1 若返回栈变量的引用
      • 1.7.2 若返回静态变量或全局变量引用
    • 1.8 数组的引用
    • 1.9 指针的引用
    • 1.10 const 引用
    • 1.11 常量的引用
    • 1.11 const 引用的原理
    • 1.12 引用的注意事项

1 引用

1.1 变量名

  1. 变量名实质上是一段连续存储空间的别名,是一个标号
  2. 通过变量名来申请并命名内存空间
  3. 通过变量的名字可以使用内存空间

1.2 引用的概念

  1. 变量名,本身是一段内存的引用,即别名(alias)。引用可以看作一个已定义变量的别名。

  2. 引用的语法:Type &name = var;
    用法如下:

int a = 10;		// 编译器分配4个字节的内存空间,a为此内存空间的别名,可以通过a来修改内存空间的值
int &b = a;		// b为a的引用,即b为a的别名,同样可以通过b来修改内存空间的值

1.3 规则

  • 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
  • 声明的时候必须初始化,一经声明,不可变更。
  • 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。 
  • &符号前有数据类型时,是引用。其它皆为取地址。
int a, b;
int &r = a;		// 正确,变量与引⽤用具有相通的地址
int &r = b;		// 错误,不可以修改原有的引用关系
float &rr = b;	// 错误,引用类型不匹配
int &ra = r;	// 正确,可以对引用再次引用,此时a有两个别名r和ra

1.4 引用作为函数参数

普通引用在声明时必须用其它的变量进行初始化,引用作为函数参数声明时不进行初始化。

void test01(int &i)	// 形参为引用,相当于对传入形参起别名
{
     
	r = 100;	// 相当于修改了主函数中a为100
	return;
}
void test02(int j)
{
     
	j = 1000;
}
int main()
{
     
	int a = 10;
	cout << a << endl;	// a = 10
	test01(a);	// 当传a进去时,i为a的别名,i与a为相同的内存地址
	cout << a << endl;	// a = 100;
	test02(a);	
	cout << a << endl;	// a = 100;
}

1.5 引用的意义

  • 引用作为其它变量的别名而存在,因此在一些场合可以代替指针
  • 引用相对于指针来说具有更好的可读性和实用性

1.6 引用的本质

  • 引用在C++中的内部实现是一个常指针

Type&  name <===> Type  const  name*

  • C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同
  • 从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏。

间接赋值的3各必要条件

  1. 定义两个变量 (一个实参一个形参)
  2. 建立关联 实参取地址传给形参
  3. *p形参去间接的修改实参的值

引用在实现上,只不过是把:间接赋值成立的三个条件的后两步和二为一.
当实参传给形参引用的时候,只不过是c++编译器帮我们程序员手工取了
一个实参地址,传给了形参引用(常量指针) 。

1.7 引用作为函数的返回值

1.7.1 若返回栈变量的引用

不能成为其它引用的初始值(不能作为左值使用)
尽量不要返回栈变量作引用

#include 
using namespace std;

int getA1()
{
     
    int a;
    a = 10; 
    return a;
}

int& getA2()
{
     
    int a;
    a = 10; 
    return a;
}

int main()
{
     
    int a1 = 0;
    int a2 = 0;

    // 值拷贝
    a1 = getA1();

    // 将一个引用赋给一个变量,会有拷贝动作
    // 理解:编译器类似做了如下隐藏操作,a2 = *(getA2());
    a2 = getA2();

    // 将一个引用赋给另一个引用作为初始值,由于是栈内存的引用(函数getA2中变量a为栈内存,出了作用域就栈内存就释放了),内存非法
    //int &a3 = getA2();
      
    cout << "a1 = " << a1 << endl;
    cout << "a2 = " << a2 << endl;
//    cout << "a3 = " << a3 << endl;

	return 0;
}

1.7.2 若返回静态变量或全局变量引用

可以成为其他引用的初始值(可作为右值使用,也可作为左值使用)

#include 
using namespace std;

int getA1()
{
     
    int a;
    static a = 10; 
    return a;
}

int& getA2()
{
     
    int a;
    static a = 10; 
    return a;
}

int main()
{
     
    int a1 = 0;
    int a2 = 0;

    // 值拷贝
    a1 = getA1();

    // 将一个引用赋给一个变量,会有拷贝动作
    // 理解:编译器类似做了如下隐藏操作,a2 = *(getA2());
    a2 = getA2();

    // 将一个引用赋给另一个引用作为初始值,由于是静态区域,内存合法
    int &a3 = getA2();
 
    cout << "a1 = " << a1 << endl;
    cout << "a2 = " << a2 << endl;
    cout << "a3 = " << a3 << endl;

	return 0;
}
  1. 如果返回值为引用可以当左值。(返回变量本身)(全局/静态变量)
  2. 如果返回值为普通变量不可以当左值。(返回变量的值)
#include 
using namespace std;

// 函数当左值
// 返回变量的值
int func1()
{
     
    int a;
    static a = 10; 
    return a;
}

// 返回变量本身
int& func2()
{
     
    int a;
    static a = 10; 
    return a;
}

int main()
{
     
    int a1 = 0;
    int a2 = 0;

    // 函数当右值
    a1 = func1();
	cout << "a1 = " << a1 << endl;	// a1 = 10
	
	// 函数返回值是一个引用,并且当右值
    a2 = func2();
	cout << "a2 = " << a2 << endl;	// a2 = 10
	
	// 函数当左值
	//func1 = 100;	// 错误
	func2 = 100;	// 函数返回值是⼀一个引⽤用,并且当左值
	
	int a3 = func2();
	cout << "a3 = " << a3 << endl;	// a3 = 100
	
	return 0;
}

1.8 数组的引用

//1.直接建立引用
int arr[10];
int(&pArr)[10] = arr;
// 使用
pArr[0] = 1;	// 相当于arr[0] = 1;
----------------------------------------------------------------
//2.先定义出数组类型,再通过类型 定义引用
int arr[10];
typedef int(ARRAY_TYPE)[10];	// 创建出10个int类型的数组,叫ARRAY_TYPE,即ARRAY_TYPE为10个int类型的数组
ARRAY_TYPE & pArr2 = arr;
// 使用
pArr2[0] = 1;	// 相当于arr[0] = 1;

1.9 指针的引用

#include 
using namespace std;

struct Person
{
     
    string name;
    int age;
};

void getPerson01(Person **p)
{
     
    (*p)->name = "张三";
    (*p)->age  = 18; 
    return;
}

void getPerson02(Person *&p)
{
     
    p->name = "李四";
    p->age  = 20; 
    return;
}

int main()
{
     
    Person *pP = new Person;

	//1	c语⾔言中的⼆二级指针
    getPerson01(&pP);
    cout << "姓名:" << pP->name << " 年龄:" << pP->age << endl;

	//2	c++中的引⽤用 (指针的引⽤用)
	//引⽤用的本质 间接赋值后2个条件 让c++编译器帮我们程序员做了
    getPerson02(pP);
    cout << "姓名:" << pP->name << " 年龄:" << pP->age << endl;

    delete pP; 
    return 0;
}

利用引用可以简化指针
可以直接用同级指针的引用,给同级指针分配空间

1.10 const 引用

const 引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。

1.const 对象的引用必须是const的,将普通引用绑定到const对象是不合法的。

const int a =1;	// const变量
int &b = a;		// 普通引用b绑定到const对象不合法
------------------------------------------------------------
int x = 1;
const int &y = a;	// 合法,常引⽤用是限制变量为只读 不能通过y去修改x了
// y = 2;			// 错误
  1. const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是const 引用与普通引用最大的区别。
const int a =1;	// const变量
const int &b = a;	// 合法
------------------------------------------------------------
double x = 3.14;
const int &y = x;	// 合法  y = 3;

1.11 常量的引用

const int &ref = 10;
// 加了const之后,相当于写成 int temp = 10;  const int &ref = temp;

//常量引用的使用场景:修饰函数中的形参,防止误操作
void test(const int &a)
{
     
	a = 10;	// 错误
}

1.11 const 引用的原理

const 引用的目的是,禁止通过修改引用值来改变被引用的对象。
const 引用的初始化特性较为微妙,可通过如下代码说明:

double val = 3.14;
const int &ref = val;	// 使用相关类型的对象初始  相当于 int temp = val; const int &ref = temp;
double &ref2 = val;
cout << ref << " " << ref2 << endl;
val = 4.14;
cout << ref << " " << ref2 << endl;

上述输出结果为3  3.14和3  4.14。因为ref是const的,在初始化的过程中已经给定值,不允许修改.而被引用的对象是val,是非const的,所以val的修改并未影响ref的值,而ref2的值发生了相应的改变。

为什么非const的引用不能使用相关类型(常量,非同类型的变量或表达式)初始化呢???
实际上,const 引用使用相关类型对象初始化时发生了如下过程: 

 int temp = val; 
 const int &ref = temp; 

如果 ref 不是const的,那么改变ref 值,修改的是 temp,而不是 val。期望对ref的赋值会修改val的程序员会发现val实际并未修改。
因此不允许使用相关类型初始化非const引用

// 1.⽤用变量 初始化 常引⽤用
int x1 = 30;
const int &y1 = x1;	// 用x1变量初始化  常引用

// 2.用字面量初始 常量引用
const int a = 40;	// c++编译器把a放在符号表中

// int &m = 41;		// 错误
const int &m = 42;	// c++ 会分配内存空间
					// 相当于 int temp = 42; const int &m = temp;

1.12 引用的注意事项

  1. 引用必须引一块合法内存空间
  2. 不要返回局部变量的引用
  3. 当函数返回值是引用时候,那么函数的调用可以作为左值进行运算

结论:

  1. const int & e 相当于 const int * const e
  2. 普通引用 相当于 int *const e
  3. 当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
  4. 使用字面量对const引用初始化后,将生成一个只读变量

你可能感兴趣的:(#,C++对C语言的扩展,c++,c语言,引用传递,linux)