引用==》引用型返回值==》{返回左值 不能返回局部变量的引用}
常引用型返回值==》返回右值
引用型返回值
返回左值
值形式的函数返回值通常都具有右值属性,即在函数的调用者空间根据函数的返回类型创建一个匿名对象,负责接收该函数的返回值
用于接收函数返回值的匿名对象和表达式的值类似,通常只具有语句级生命期且只能,即所谓将亡右值
如果函数返回的是一个引用,那么用于接收该返回值的就不再是一个匿名的将亡右值对象,而是一个引用该函数所返回的目标对象的引用,甚至可以是左值引用
函数返回的左值引用可与其他任何形式的左值一样,参与包括赋值,复合赋值,自增减等在内的各种左值运算
不能返回局部变量的引用
函数的局部变量只具有函数级甚至块或语句块的生命周期,函数一旦返回,所有的局部变量即刻销毁,即使通过返回值获得了对他们的引用,其目标也将是未定义的
–int& foo(void){int n=123;return n;}//危险
int* bar(void){int n=456;return &n;}//危险
int hum(void){int n=789;return n;}
–int& foo(int& n){return n;}
int& bar(int* n){return n};
int& hum(int n){return n;}//危险
–int foo(int& n){return &n};
int* bar(int* n){return n;}
int* hum(int n){return &n;}//危险
常引用型返回值
返回右值
值形式的函数返回值天然具有右值属性,但从函数返回值跟通过值向函数传参数一样,伴随着对象的复制过程
–int foo(void){…}
foo()=1234;//错误
为了避免对象复制的开销,同时又不想失去作为函数返回值的右值属性,可以返回一个常左值引用,模拟或者逼近右值的使用效果
–int& foo(void){…}
foo()=1234;
–int const& foo(void){…}
foo()=1234;//错误
类型转换==》显式类型转换==》静态类型转换,动态类型转换,去长类型装换,重解释类型转换
静态类型转换
static_cast<目标类型>(源类型对象)
–编译器对源类型和目标类型做相容性检查,检查不通过报错
–源类型和目标类型只要在一个放向上可以做隐式类型转换,那么在两个方向上就都可以做静态类型转换
–如果将目标类型从源类型的类型转换构造函数,或者源类型向目标类型的类型转换运算符函数,被声明为explicit,那么从源类型到目标类型的类型转换就必须显式完成,静态类型转换可用于这样的场合
动态类型转换
dynamic_cast<目标类型>(源类型对象)
–编译器首先检查源类型和目标类型是否同为指针或引用且其类型之间存在具有多态性的继承关系,不存在直接报错
–编译器生成一段指令,运行时执行该指令,检查源和目标的类型是否一致,不一致通过返回空指针或抛出异常报错
–常被用于具有多态继承关系的父子类对象的指针或引用之间的转换
==》将泛型指针转换为具体指针
去常类型转换
const_cast<目标类型>(源类型对象)
–编译器检查源类型和目标类型是否同为指针或引用,且其目标类型之间除常属性以外必须完全相同,否则直接报错
–去除指针或引用上的const属性
int const volatile x=100;
int const* p=&x;
p =200;//错误
int q=const_cast<int *>§;
*q=200;
>通过去常修改只读变量的值》通过去常修改只读变量的值
重解释类型转换
reinterpret_cast<目标类型>(源类型对象)
–编译器检查源类型和目标类型是否同为指针或引用,或者一个是指针一个是整型,否则直接报错
–在任意类型的指针或引用之间转换,意味着可以将同一个对象视作不同的类型,并以不同的方式访问或处理之
–无论何种类型的指针,从本质上讲都与整数无异,即地址空间中一个特定字节的顺序
目录
返回有效引用
将泛型指针转换为具体指针
通过去常修改预期变量的值
从低地址到高地址打印多字节变量
1返回有效引用
1.1问题
函数返回值通常都具有右值属性,在函数的调用者空间下根据函数的返回类型创建一个匿名对象,负责接收该函数的返回值,该匿名对象一般是临时对象。如果函数返回的是一个引用,那么用作接收该返回值的就不再是一个临时对象,或者一个引用该函数所返回的引用的目标对象的引用,甚至可以是左值引用。
1.2步骤
实现此案例需要按照如下步骤进行。
步骤一:返回变量,静态,成员变量的引用
代码如下所示:
#include < iostream >
int g = 123 ;
struct Dummy {
int m ;
int & foo ( void ) { 返回 g ; }
int & bar ( void ) { static int s = 456 ; 返回 s ; }
int & hum ( void ) { 返回 m ; }
} ;
int main ( int argc , const char * argv [])
{
虚拟假人= { 789 } ;
int & rg =虚拟。foo ();
std :: cout << “ rg =” << rg << std :: endl ;
int & rs =虚拟。bar ();
std :: cout << “ rs =” << rs << std :: endl ;
int & rm =虚拟。哼 ();
std :: cout << “ rm =” << rm << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
int & foo ( void ) { 返回 g ; }
定义了结构体Dummy的一个成员函数,该函数返回变量变量g的一个引用。
上述代码中,以下代码:
int & rg =虚拟。foo ();
定义了一个引用rg,该引用通过返回引用的函数foo替换成为变量的g的别名,所以,附带语句:
std :: cout << “ rg =” << rg << std :: endl ;
输出的是变量变量g的值。
上述代码中,以下代码:
int & bar ( void ) { static int s = 456 ; 返回 s ; }
定义了结构体Dummy的一个成员函数,该函数返回静态局部整型变量s的一个引用。
上述代码中,以下代码:
int & rs =虚拟。bar ();
定义了一个引用rs,该引用通过返回引用的函数bar变为静态局部整型变量的别名,所以,附带语句:
std :: cout << “ rs =” << rs << std :: endl ;
输出的是静态局部整型变量s的值。
上述代码中,以下代码:
int & hum ( void ) { 返回 m ; }
定义了结构体Dummy的一个成员函数,该函数返回结构体成员变量m的一个引用。
上述代码中,以下代码:
int & rm =虚拟。哼 ();
定义了一个引用rm,该引用通过返回引用的函数hum将成为机构体成员变量m的别名,所以,包含语句:
std :: cout << “ rm =” << rm << std :: endl ;
输出的是结构体成员变量m的值。
步骤二:返回调用对象本身的引用
代码如下所示:
#include < iostream >
int g = 123 ;
struct Dummy {
int m ;
int & foo ( void ) { 返回 g ; }
int & bar ( void ) { static int s = 456 ; 返回 s ; }
int & hum ( void ) { 返回 m ; }
假人& up ( void ) { ++ m ; 返回 * 这个; }
} ;
int main ( int argc , const char * argv [])
{
虚拟假人= { 789 } ;
int & rg =虚拟。foo ();
std :: cout << “ rg =” << rg << std :: endl ;
int & rs =虚拟。bar ();
std :: cout << “ rs =” << rs << std :: endl ;
int & rm =虚拟。哼 ();
std :: cout << “ rm =” << rm << std :: endl ;
假人。向上()。向上()。上();
std :: cout << “ rm =” << rm << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
假人& up ( void ) { ++ m ; 返回 * 这个; }
定义了结构体Dummy的一个成员函数,该函数返回结构体自身* this的一个引用。
上述代码中,以下代码:
假人。向上()。向上()。上();
相当于写成如下方式:
(假人。up ())。向上()。上();
因为dummy.up()函数的返回值就是dummy本身,所以(dummy.up())。up()在第一个up函数返回后等效于(dummy).up(),依次类推。
步骤三:返回堆变量的引用
代码如下所示:
#include < iostream >
int g = 123 ;
struct Dummy {
int m ;
int & foo ( void ) { 返回 g ; }
int & bar ( void ) { static int s = 456 ; 返回 s ; }
int & hum ( void ) { 返回 m ; }
假人& up ( void ) { ++ m ; 返回 * 这个; }
} ;
整数和 平方 (整数x ) {
int * y = 新的 int ;
* y = x * x ;
返回 * y ;
}
int main ( int argc , const char * argv [])
{
虚拟假人= { 789 } ;
int & rg =虚拟。foo ();
std :: cout << “ rg =” << rg << std :: endl ;
int & rs =虚拟。bar ();
std :: cout << “ rs =” << rs << std :: endl ;
int & rm =虚拟。哼 ();
std :: cout << “ rm =” << rm << std :: endl ;
假人。向上()。向上()。上();
std :: cout << “ rm =” << rm << std :: endl ;
int & y = 平方(16 );
std :: cout << y << std :: endl ; // 256
删除 & y ;
返回 0 ;
}
上述代码中,以下代码:
整数和 平方 (整数x ) {
int * y = 新的 int ;
* y = x * x ;
返回 * y ;
}
定义了一个返回堆上变量引用的函数,在该函数体中,在堆上申请了一块儿存储空间,并返回该存储空间的引用。
上述代码中,以下代码:
int & y = 平方(16 );
定义了一个引用y,该引用是对上申请的一块儿空间的别名。引用y是一种“野引用”或“悬空引用”。
步骤四:返回引用型参数本身
代码如下所示:
#include < iostream >
int g = 123 ;
struct Dummy {
int m ;
int & foo ( void ) { 返回 g ; }
int & bar ( void ) { static int s = 456 ; 返回 s ; }
int & hum ( void ) { 返回 m ; }
假人& up ( void ) { ++ m ; 返回 * 这个; }
} ;
整数和 平方 (整数x ) {
int * y = 新的 int ;
* y = x * x ;
返回 * y ;
}
int const & max ( int const & a , int const & b )
{
返回 a > b ?a : b ;
}
int main ( int argc , const char * argv [])
{
虚拟假人= { 789 } ;
int & rg =虚拟。foo ();
std :: cout << “ rg =” << rg << std :: endl ;
int & rs =虚拟。bar ();
std :: cout << “ rs =” << rs << std :: endl ;
int & rm =虚拟。哼 ();
std :: cout << “ rm =” << rm << std :: endl ;
假人。向上()。向上()。上();
std :: cout << “ rm =” << rm << std :: endl ;
int & y = 平方(16 );
std :: cout << y << std :: endl ; // 256
删除 & y ;
int a = 123 , b = 456 ;
std :: cout << &a << '' << & b << std :: endl ;
int const & c = max ( a , b );
std :: cout << 和 c << “:” << c << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
int const & max ( int const & a , int const & b )
{
返回 a > b ?a : b ;
}
定义了一个返回常引用的函数,该函数有两个常引用形参,返回的是大的那个常引用形参。
上述代码中,以下代码:
int const & c = max ( a , b );
定义了一个常引用c,替换为整型变量a或b的别名,至于最终是哪一个变量的别名,而是其中整型变量的值大。在本例中,由于a被初始化变为123 ,b被初始化变成456,b大于a,所以常引用c就是变量b的别名。
步骤五:不能返回局部变量的引用
代码如下所示:
#include < iostream >
int g = 123 ;
struct Dummy {
int m ;
int & foo ( void ) { 返回 g ; }
int & bar ( void ) { static int s = 456 ; 返回 s ; }
int & hum ( void ) { 返回 m ; }
假人& up ( void ) { ++ m ; 返回 * 这个; }
} ;
整数和 平方 (整数x ) {
int * y = 新的 int ;
* y = x * x ;
返回 * y ;
}
int const & max ( int const & a , int const & b )
{
返回 a > b ?a : b ;
}
int 和 foo ( void )
{
int n = 123 ;
返回 n ;
}
int main ( int argc , const char * argv [])
{
虚拟假人= { 789 } ;
int & rg =虚拟。foo ();
std :: cout << “ rg =” << rg << std :: endl ;
int & rs =虚拟。bar ();
std :: cout << “ rs =” << rs << std :: endl ;
int & rm =虚拟。哼 ();
std :: cout << “ rm =” << rm << std :: endl ;
假人。向上()。向上()。上();
std :: cout << “ rm =” << rm << std :: endl ;
int & y = 平方(16 );
std :: cout << y << std :: endl ; // 256
删除 & y ;
int a = 123 , b = 456 ;
std :: cout << &a << '' << & b << std :: endl ;
int const & c = max ( a , b );
std :: cout << 和 c << “:” << c << std :: endl ;
int & z = foo ();
std :: cout << “ z =” << z << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
int 和 foo ( void )
{
int n = 123 ;
返回 n ;
}
是错误的。因为函数foo返回了局部变量n的引用。由于当函数foo运行结束时,变量n所占的存储空间已经被释放,所以返回的引用就没有一块儿合法的存储空间相对应了,这样必然带来不测的后果。所以下面的代码中:
int & z = foo ();
引用z所对应的存储空间是一块儿已经被释放的存储空间,这样使用z将是危险的。
1.3完整代码
本案例的完整代码如下所示:
#include < iostream >
int g = 123 ;
struct Dummy {
int m ;
int & foo ( void ) { 返回 g ; }
int & bar ( void ) { static int s = 456 ; 返回 s ; }
int & hum ( void ) { 返回 m ; }
假人& up ( void ) { ++ m ; 返回 * 这个; }
} ;
整数和 平方 (整数x ) {
int * y = 新的 int ;
* y = x * x ;
返回 * y ;
}
int const & max ( int const & a , int const & b )
{
返回 a > b ?a : b ;
}
int 和 foo ( void )
{
int n = 123 ;
返回 n ;
}
int main ( int argc , const char * argv [])
{
虚拟假人= { 789 } ;
int & rg =虚拟。foo ();
std :: cout << “ rg =” << rg << std :: endl ;
int & rs =虚拟。bar ();
std :: cout << “ rs =” << rs << std :: endl ;
int & rm =虚拟。哼 ();
std :: cout << “ rm =” << rm << std :: endl ;
假人。向上()。向上()。上();
std :: cout << “ rm =” << rm << std :: endl ;
int & y = 平方(16 );
std :: cout << y << std :: endl ; // 256
删除 & y ;
int a = 123 , b = 456 ;
std :: cout << &a << '' << & b << std :: endl ;
int const & c = max ( a , b );
std :: cout << 和 c << “:” << c << std :: endl ;
int & z = foo ();
std :: cout << “ z =” << z << std :: endl ;
返回 0 ;
}
2将泛型指针转换为具体指针
2.1问题
static_cast <目标类型>(源类型对象)
编译器对源类型和目标类型做相容性检查,检查不通过报错。
2.2步骤
实现此案例需要按照如下步骤进行。
步骤一:将泛型指针转换为具体指针
代码如下所示:
#include < iostream >
int main ( int argc , const char * argv [])
{
短x = 10 ;
无效* v = & x ;
简短* p = static_cast <简短*> ( v );
std :: cout << “ x =” << * p << std :: endl ;
短x1 = 10 , * v1 = & x1 ;
int * p1 = static_cast < int *> ( v1 ); //错误
std :: cout << “ x1 =” << * p1 << std :: endl ;
短x2 = 10 , y2 = 20 ;
无效* v2 = & y2 ;
int * p2 = static_cast < int *> ( v2 );
* p2 = 0 ;
std :: cout << 和 x2 << “,” << & y2 << std :: endl ;
std :: cout << “ x2 =” << x2 << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
简短* p = static_cast <简短*> ( v );
源类型(void *)和目标类型(short *)只要在一个方向上可以做隐式类型转换,如,void * v =&x ;,那么在两个方向上就都可以做静态类型转换。
上述代码中,以下代码:
int * p1 = static_cast < int *> ( v1 ); //错误
源类型(short *)和目标类型(int *)若在两个方向上都不能做隐式类型转换,如,int * p1 = v1 ;,那么在两个方向上也都不能做静态类型转换。
上述代码中,以下代码:
短x2 = 10 , y2 = 20 ;
无效* v2 = & y2 ;
int * p2 = static_cast < int *> ( v2 );
虽然没有* v2可以静态类型转换成整型指针p2,但由于v2原来是指向短类型变量的指针,所以静态类型转换语法上没有问题,但会生成运行上的逻辑错误,如以下语句:
* p2 = 0 ;
由于* p2等于一个整型变量,所以将* p2清零会变量4个字节变成零。我们通过观察短整型变量x2和y2的地址,如以下语句:
std :: cout << 和 x2 << “,” << & y2 << std :: endl ;
可以发现短整型变量y2所占的2个字节后面紧接着就是x2所占的2个字节,这样从y2的第一个字节开始清零,连续4个字节,x2也被清零了。可以通过如下语句验证:
std :: cout << “ x2 =” << x2 << std :: endl ;
2.3完整代码
本案例的完整代码如下所示:
#include < iostream >
int main ( int argc , const char * argv [])
{
短x = 10 ;
无效* v = & x ;
简短* p = static_cast <简短*> ( v );
std :: cout << “ x =” << * p << std :: endl ;
短x1 = 10 , * v1 = & x1 ;
int * p1 = static_cast < int *> ( v1 ); //错误
std :: cout << “ x1 =” << * p1 << std :: endl ;
短x2 = 10 , y2 = 20 ;
无效* v2 = & y2 ;
int * p2 = static_cast < int *> ( v2 );
* p2 = 0 ;
std :: cout << 和 x2 << “,” << & y2 << std :: endl ;
std :: cout << “ x2 =” << x2 << std :: endl ;
返回 0 ;
}
3通过去常修改预期变量的值
3.1问题
const_cast <目标类型>(源类型对象)
编译器检查源类型和目标类型是否同为指针或引用,并且其目标类型之间除常属性以外必须完全相同,否则直接报错。
3.2步骤
实现此案例需要按照如下步骤进行。
步骤一:通过去常修改预期变量的值
代码如下所示:
#include < iostream >
int main ( int argc , const char * argv [])
{
int x = 100 ;
int const * cp = & x ;
* cp = 200 ; //错误
int * p = const_cast < int *> ( cp );
* p = 200 ;
std :: cout << “ x =” << x << std :: endl ;
std :: cout << “ * p =” << * p << std :: endl ;
int x1 = 100 ;
int const & cr = x1 ;
cr = 200 ; //错误
int & r = const_cast < int &> ( cr );
r = 200 ;
std :: cout << “ x1 =” << x1 << std :: endl ;
std :: cout << “ r =” << r << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
* cp = 200 ; //错误
错误是因为不能通过指针cp修改指针cp所指向的存储单元。
上述代码中,以下代码:
int * p = const_cast < int *> ( cp );
因为源类型(int const *)和目标类型(int *)同为指针,并且除常属性以外必须完全相同,所以可以进行去常类型转换,只是指针p没有了常属性。
上述代码中,以下代码:
cr = 200 ; //错误
错误是因为cr为常引用,即不能做左值使用。
上述代码中,以下代码:
int & r = const_cast < int &> ( cr );
因为源类型(int const&)和目标类型(int&)同为引用,并且除常属性以外必须完全相同,所以可以进行去常类型转换,只是引用r没有了常属性。
3.3完整代码
本案例的完整代码如下所示:
#include < iostream >
int main ( int argc , const char * argv [])
{
int x = 100 ;
int const * cp = & x ;
* cp = 200 ; //错误
int * p = const_cast < int *> ( cp );
* p = 200 ;
std :: cout << “ x =” << x << std :: endl ;
std :: cout << “ * p =” << * p << std :: endl ;
int x1 = 100 ;
int const & cr = x1 ;
cr = 200 ; //错误
int & r = const_cast < int &> ( cr );
r = 200 ;
std :: cout << “ x1 =” << x1 << std :: endl ;
std :: cout << “ r =” << r << std :: endl ;
返回 0 ;
}
4从低地址到高地址打印多字节变量
4.1问题
reinterpret_cast <目标类型>(源类型对象)
编译器检查源类型和目标类型是否同为指针或引用,或者一个是指针一个是整型,否则直接报错。
4.2步骤
实现此案例需要按照如下步骤进行。
步骤一:从低地址到高地址打印多字节变量
代码如下所示:
#include < iostream >
int main ( int argc , const char * argv [])
{
整数n = 0x12345678 ;
字符* p ;
p = reinterpret_cast < char *>(& n );
对于 ( int i = 0 ; i < 4 ; i ++, p ++)
std :: cout << std :: hex << ( int )* p << “”“ << std :: endl ;
std :: cout << std :: endl ;
返回 0 ;
}
上述代码中,以下代码:
p = reinterpret_cast < char *>(& n );
源类型(int *)和目标类型(char *)同为指针,即可进行重解释类型转换。
上述代码中,以下代码:
对于 ( int i = 0 ; i < 4 ; i ++, p ++)
std :: cout << std :: hex << ( int )* p << “”“ << std :: endl ;
因为n为整型变量,整型变量在进行内存操作时一次要处理4个字节。而指针p为字符型指针,则字符变量* p在进行内存操作时一次只处理1个字节,所以要循环4次才能将整型变量n的值全部输出。
对于整型变量n中的值0x12345678在内存中的存放方法是从低地址到高地址存放的,即第一个字节存0x78,第二个字节存0x56,第三个字节存0x34,第四个字节存0x12。指针p指向变量n的第一个字节,这样就可以从低地址到高地址打印整型变量n内的多字节内容了。
4.3完整代码
本案例的完整代码如下所示:
#include < iostream >
int main ( int argc , const char * argv [])
{
整数n = 0x12345678 ;
字符* p ;
p = reinterpret_cast < char *>(& n );
对于 ( int i = 0 ; i < 4 ; i ++, p ++)
std :: cout << std :: hex << ( int )* p << “”“ << std :: endl ;
std :: cout << std :: endl ;
返回 0 ;
}