字符串相关

实例代码:

//试看下面的代码:
#include <iostream>
using namespace std;

void f(int &a)

 {
 cout << "f(" << a  << ") is being called" << endl;
}

void g(const int &a)

{
 cout << "g(" << a << ") is being called" << endl;
}

 int main()

 {
 int a = 3, b = 4;
 f(a + b);  //编译错误,把临时变量作为非const的引用参数,传递给int &a了
 g(a + b);  //OK,把临时变量作为const&传递是允许的
}

上面的两个调用之前,a+b的值会存在一个临时变量中,因为a+b是一个表达式,本质上属于一个没有名字的变量,编译器会自动生成一个临时变量储存a+b的值,当把这个临时变量传给f时,由于f的声明中,参数是int&,不是常量引用,所以产生以下编译错误:

const_ref.cpp: In function `int main()':
const_ref.cpp:14: error: invalid initialization of non-const reference of type '
  int&' from a temporary of type 'int'
const_ref.cpp:4: error: in passing argument 1 of `void f(int&)'

关于临时变量的内容,推荐博客:

临时变量不能作为非const类型引用形参的实参 - jiayouwyhit - 博客园www.cnblogs.com/jiayouwyhit/p/3681224.html

而在g(a+b)中,由于g定义的参数是const int&,编译通过。 问题是为什么临时变量作为引用参数传递时,必须是常量引用呢?

很多人对此的解释是临时变量是常量,不允许赋值,改动,所以当作为非常量引用传递时,编译器就会报错。这个解释在关于理解【临时变量】不能作为【非const引用参数】这个问题上是可以的,但不够准确。事实上,临时变量是可以被作为左值(L value)并被赋值的,请看下面的代码:

#include  <iostream> 
using namespace std;

class CComplex {  
    friend CComplex operator+(const CComplex &cp1, const CComplex &cp2);
    friend ostream& operator<<(ostream &os, const CComplex &cp);
    private: 
        int x; 
    public: 
        CComplex(){}
        CComplex(int x1) { 
            x = x1; 
        }
};

CComplex operator+(const CComplex &cp1, const CComplex &cp2){ 
    CComplex cp3; 
    cp3.x = cp1.x + cp2.x; 
    return cp3;                 //返回的cp3其实是一个函数内部的临时变量,在外部并没有名字,由(a+b)接收。
} 

ostream& operator<<(ostream &os, const CComplex &cp){
    os << cp.x;
    return os;
}

int main(){ 
    CComplex a(2), b(3), c(4); 
    cout << (a + b) << endl;
    cout << ((a + b) = c) << endl;  //临时对象作为左值
    return 0; 
}

上面的程序编译通过,而且运行结果是:

5
4

临时变量确实被赋值,而且成功了。上述的例子说明了临时变量本身其实是可以被修改的。问题出在引用上,也就是说编译器不允许引用一个非const类型的临时变量。

The compiler won’t allow the reference toward a temporary variable which is not constant.

所以,【临时变量】不能作为【非const引用参数】,不是因为他是常量,**而是因为c++编译器的一个关于语义的限制。**如果一个参数是以非const引用传入,**c++编译器就有理由认为程序员会在函数中修改这个值,并且这个被修改的引用在函数返回后要发挥作用。**但如果你把一个临时变量当作非const引用参数传进来,由于临时变量的特殊性,程序员并不能操作临时变量,而且临时变量随时可能被释放掉,所以,一般说来,修改一个临时变量是毫无意义的,据此,c++编译器加入了临时变量不能作为非const引用的这个语义限制,意在限制这个非常规用法的潜在错误。

以上内容源自博客:

c++中临时变量不能作为非const的引用参数 - Dec-Fth - 博客园www.cnblogs.com/faith0217/articles/4076296.html

在了解了以上内容之后,可以看下面一个例子:

#include <iostream>

using namespace std;

class A{
    private:
        int length;

    public:
        A(){
        length = 100;
        }

        A(int len){
        length = len;
        }

        void setLength(int len){
            length = len;
        }

        A operator+(const A& temp){
            int total_length = this->length + temp.length;
            return A(total_length);
        }

        int getLength(){
            return length;
        }
};

int main(){
    A a = A();
    A b = A(10);
    A c = a+b;
    cout<<"this length of c is: "<<c.getLength()<<endl;
    return 0;
}

若将其中的:

A operator+(const A& temp){
    //code
    return A(total_length);
}

修改为如下的代码:

A& operator+(const A& temp){
    //code
    return A(total_length);
}

则会报错:

error: cannot bind non-const lvalue reference of type 'A&' to an rvalue of type 'A'
    return A(total_length);
           ^

这是因为构造一个对象A(total_length),这个对象并没有名字,而是作为一个临时变量返回的,但是返回的类型确实一个引用,根据前文提到的C++语法规定,我们无法将【引用】与一个【非const的临时变量】进行绑定,所以会像这样报错。

需要注意的是,第一个例子中,f(a+b)实际上是将a+b作为一个整体传递进去,编译器会自动分配一个临时变量(temp = a+b)。而重载的操作符+的工作原理是将this指针(指向第一个操作数a)和第二个操作数(对象b)的引用传递进函数,这一过程并不涉及临时变量。

函数return引用类型的语法规范 #

由于函数本身处在的栈空间会在执行完之后被释放,所以返回的引用不可能指向本地栈空间内的变量,所以要么是该函数外部的变量,要么是函数内部动态分配的内存空间中的变量。

下面是一个错误实例(really nasty code):

A* & operator+(const A& temp){ //肯定不会有人这么写的,权当测试了
    //code
    A* temp = new A(total_length);
    A* & res = temp;
    return res;
}

int main(){
    A a = A(100);
    A b = A(10);
    A* & c = a+b
}

可以看到,即使我们采用了动态内存分配,还是会报错。我们尝试返回*一个指针变量的引用,但是由于指针变量本身依旧是本地变量,指针指向的堆空间不会被销毁,但是指针变量本身是会随着return被释放的。

综上所述,一个函数想要以一个引用作为返回值,需要满足被引用的变量不会随着函数的结束而被释放,例如:

//将 int getLength(){return length;} 改为
int& getLength(){ return length;}

其中length作为对象本身的数据,在main结束之后才会消亡,所以可以放心地返回length的引用,这种做法常见于数据类型为向量的情况中,为了避免大规模的数据拷贝,类的接口函数往往会返回自身数据成员的引用。

当然当我们重载操作符的时候,往往希望返回一个类的对象,假如这个类的大小足够大以至于直接返回对象本身开销很大,还可以进行如下操作:

A* operator+(const A& temp){
    //code
    A* temp = new A(total_length);
    return temp;
}

假如没有动态分配内存,实际上编译器会做一个bitwise的拷贝,将class A的一个对象拷贝到函数调用者(如main函数)的内存空间中:

//some functions defined here
int main(){
    A instance = f();   //f()返回:return A(); 那么A()这个对象会被bitwise拷贝到变量instance的空间中。
}
comments powered by Disqus