引用

来源:互联网 发布:好用的数据采集器软件 编辑:程序博客网 时间:2024/06/05 14:20

reference : C++ Primer Plus

2.1 创建引用变量int rats= 101;int &rodents = rats; //rodents是rats的引用。rodents的类型为int &,即指向int变量的引用。上述引用申明允许将rats和rodents互换,他们指向相同的值和内存单元。&不是地址运算符,而是类型标识符的一部分,就像char*指的是指向char的指针一样。rodents和rats的地址相同,即&rodents和rats相同。引用与指针:引用必须在申明时进行初始化,而指针可以不用这样做。引用更接近const指针,必须在创建时初始化,一旦与某个变量关联起来,就将一直效忠于它。也就是说:    int &rodents = rats;    实际上是下述代码的伪装表示:    int *const pr = &rats; //*在const左边,地址不可变。    其中引用rodents扮演的角色和*pr相同。例子:程序清单8.3#include "iostream"using namespace std;void main(){ int rats = 101; int &rodents = rats; cout << "rats = " << rats; cout << ", rodents = " << rodents << endl; cout << "rats address = " << &rats; cout << ", rodents address = " << &rodents << endl; int bunnies = 50; rodents = bunnies; //rodents依然是rats的引用,把bunnies赋给rodents,也就是把bunnies赋给rats,rodents就是rats。 cout << "bunnies = " << bunnies; cout << ", rats = " << rats; cout << ", rodents = " << rodents << endl; cout << "bunnies address = " << &bunnies; cout << ", rats address = " << &rats; cout << ", rodents address = " << &rodents << endl; cin.get();}


补充说明: int rats = 101; int *pt = &rats; int &rodents = *pt; int bunnies = 50; pt = &bunnies;

将rodents初始化为*pt使得rodents指向rats。接下来将pt改为指向bunnies,并不能改变这样的事实,即rodents引用的是rats。

2.2 将引用用作函数参数其他的详见书本。注意:前面说过,引用必须在申明时进行初始化。函数调用使用实参初始化形参,因此函数的引用参数被初始化为函数调用传递的实参。2.3 引用的属性和特别之处#include "iostream"using namespace std;double refcube(double &ra){ ra *= ra * ra; return ra;}void main(){ double side = 3.0; cout << refcube(side + 2.0) << endl; //在现代的C++中这是错误的,大多数编译器会编译不通过:非常量引用的初始值必须为左值。而有些较老的编译器将发出这样的警告:Warning Temporary used foe parameter 'ra' in call to refcube(double &)这样做的结果是:由于side+2.0不是double类型的变量,因此程序将创建一个临时无名变量,并将其初始化为表达式side+2.0的值。然后,ra成为该临时变量的引用。 long fuck = 3; cout << refcube(fuck) << endl; //编译不通过:无法用“long”类型的值初始化“double &”类型的引用(非常量限定)同上,在有些老编译器中这样做是可以的,这些老编译器会创建一个临时double变量,并将其初始化为3,然后在函数refcube中操作该临时变量,而fuck保持不变。 cin.get();}如果将函数refcube改写成如下形式,则函数调用refcube(side + 2.0)和refcube(fuck)就可以编译通过了。double refcube(const double &ra) //当然此时就不能改变ra的值了。{ return ra * ra * ra;}#include "iostream"using namespace std;double refcube(const double &ra){ return ra * ra * ra;}void main(){ double side = 3.0; double *pd = &side; double &rd = side; long edge = 5L; double lens[4] = { 2.0, 5.0, 10.0, 12.0 }; cout << "side = " << side << endl; cout << side << " , refcube(side) = " << refcube(side) << "\n\n" << endl; //ra is side cout << "lens[2] = " << lens[2] << endl; cout << " ,refcube(lens[2]) = " << refcube(lens[2]) << "\n\n" << endl; //ra is rd is side cout << "rd = " << rd << endl; cout << " , refcube(rd) = " << refcube(rd) << "\n\n" << endl; //ra is *pd is side cout << "*pd = " << *pd << endl; cout << " , refcube(*pd) = " << refcube(*pd) << "\n\n" << endl; //ra is temporary variable cout << "edge = " << edge << endl; cout << " , refcube(edge) = " << refcube(edge) << "\n\n" << endl; //ra is temporary variable cout << "refcube(7.0) = " << refcube(7.0) << "\n\n" << endl; //ra is temporary variable cout << "side + 2.0 = " << side + 2.0 << endl; cout << " , refcube(side + 2.0) = " << refcube(side + 2.0) << "\n\n" << endl; //ra is temporary variable cin.get();}


其实double refcube(double &ra) 和double refcube(const double &ra)是可以重载的。下面就看看这两个重载函数的调用情况,main函数不同上。double refcube(double &ra){ cout << "引用    "; ra *= ra * ra; return ra;}double refcube(const double &ra){ cout << "常引用  "; return ra * ra * ra;}


2.4 将引用用于结构引用非常适合用于结构和类。确实,引入引用主要是为了用于这些类型的,而不是基本的内置类型。将引用用于参数传递就打印显示函数display来讲,按值传递的display(结构名 &参数名)与按引用传递的display(const 结构名 &参数名)相比,前者要复制原始结构的拷贝,而后者不用,所以后者可以节省时间和内存。假设有如下结构定义:struct free_throws{ std::string name; int made; int attempt; float percent;};//函数功能:将source的内容累加到target中。free_throws &accumulate(free_throws &target, const free_throws &source){ target.attempt += source.attempt; target.made += source.made; ... return target;}void display(const free_throws &ft);    请看如下函数调用:    display(accumulate(team, two));    上述代码是什么意思呢?首先,将结构对象team传给了accumulate(),这意味着在函数accumulate()中,target指向的是team。函数accumulate()修改team,再返回指向它的引用。    如果返回类型被声明为free_throws而不是free_throws &,上述返回语句将返回target(也就是team)的拷贝。但返回类型为引用,这意味着返回的是最初传递给accumulate()的team对象。    接下来,将accumulate()的返回值作为参数传递给了display(),这意味着将team传递给了display()。display的参数为引用,这意味着函数display()中的ft指向的是team。所以,下述代码:    display(accumulate(team, two));    与下面的代码等效:    accumulate(team, two);    display(team);    上述逻辑也适用如下语句:    accumulate(accumulate(team, three), four);    因此,该语句与下面的语句等效:    accumulate(team, three);    accumulate(team, four);    再来看看下面的语句:    free_throws dup;    accumulate(dup, five) = four;这条语句将值赋给函数调用,这是可行的,因为函数的返回值是一个引用。如果函数accumulate()是按值返回的,则这条语句将不能通过编译。由于返回的是指向dup的引用,因此上述代码与下面的代码等效:    accumulate(dup, five);    dup = four;    第二条语句消除了第一条语句所做的工作,因此在原始赋值语句使用accumulate()的方式并不好。将引用用于返回值    来看下面这条语句:    free_throws dup = accumulate(team, five);    如果accumulate()返回一个结构,而不是指向结构的引用,将把整个结构复制到一个临时位置,再将这个拷贝复制给dup。但在返回值为引用时,将直接把team复制到dup,其效率更高。返回引用时需要注意的问题    返回引用时最重要的一点是,应避免返回函数终止时不再存在的内存单元的引用。您应避免编写下面这样的代码:const free_throws &clone(free_throws &ft){ free_throws  newguy; newguy = ft; return  newguy;}    该函数返回一个指向临时变量的引用,函数运行完毕后它将不再存在。    为避免这种问题,最简单的方法是返回一个作为参数传递给函数的引用。上述的accumulate()正是这样做的。    我这里插一句话哈,若将上述函数的返回类型声明为const free_throws,而不是const free_throws &,那么像下面这样的函数调用也是可以的:    const free_thorws rev = clone(one);    这里我有一个不明白的地方,就是下面的语句为何在运行时不会崩溃:    {         const free_thows &rev = clone(one);         cout << rev.name.c_str() << endl; //为何这里不会崩溃。    }    由于此时clone()返回的是局部变量,而rev正是这个局部变量的引用,而该局部变量会在clone()返回后被释放,但是为何cout << rev.name.c_str() << endl还是能够正常运行。    另一种方法是用new来分配新的存储空间。可以将上述的clone()函数代码改写成如下:const free_throws &clone(free_throws &ft){ free_throws *pt = new free_throws; *pt = ft; return *pt;}    const free_throws &jolly = clone(three);    注意下面这条语句:    free_throws &jolly = clone(three);    它在编译时会报错,因为不能将const引用赋给非const引用,但反过来却可以。    再看下面这条语句:    free_throws jolly = clone(three);    这时jolly虽然不是常量,但却可以编译通过。这是因为此时jolly有自己的内存空间,而不是指向clone()返回的引用。为何将const用于引用返回类型    前面说过这样的语句:    accumulate(dup, five) = four;    这条语句为何能够通过编译呢?在赋值语句中,左边必须是可以修改的左值。也就是说,在赋值表达式中,左边的子表达式必须标识一个可以修改的内存块。在这里,函数返回指向dup的引用,它确实标识的是一个这样的内存块,因此这条语句是合法的。    另一方面,常规(非引用)返回类型是右值——不能通过地址访问的值,如字面值(10.0)和表达式(x + y)。这种返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在。    假设您要使用引用返回值,但又不允许执行像给accumulate()赋值这样的操作,只需将返回类型声明为const引用:    const free_throws &accumulate(free_throws &target, const free_throws &source);2.5 将引用用于类对象对于下面的函数声明:string version1(const string &s1, const string &s2);请看下面的函数调用:string input = "fuckyou";string result = version1(input, "***");由于input的类型为string,因此让s1指向它没有问题。然而,程序怎么能够接受接受将char指针赋给string引用呢?这里有两点需要说明。首先,string类定义了一种char*到string的转换功能。其次是临时变量的创建,这一点与之前所说的同理。疑问:const string func(const string &str){ string rev = str; rev += "fuckyou"; return rev;}const char * func2(const string &str){ string rev = str; rev += "fuckyou"; return rev.c_str();}void main(){ string str = func("lily"); //这个可以编译通过 cout << str.c_str() << endl; char *p = func2("lucy"); //编译不通过,无法从“const char *”转换为“char *” cout << p << endl; cin.get();}疑问:为何上述两条语句,一条编译通过,而另一条编译却不通过。2.6 对象、继承和引用    继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。这中特征的一个实际结果是,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。2.7 何时使用引用参数全是文字,不想敲了,参见课本P274


0 0
原创粉丝点击