在C ++中将值作为常量,引用和常量引用返回的含义是什么?

时间:2009-04-27 23:21:41

标签: c++ return-value

我正在学习C ++,我仍然对此感到困惑。在C ++中将值作为常量,引用和常量引用返回有什么含义?例如:

const int exampleOne();
int& exampleTwo();
const int& exampleThree();

8 个答案:

答案 0 :(得分:27)

以下是您所有案件的缩减:

•按引用返回:函数调用可用作赋值的左侧。例如使用运算符重载,如果你有operator []重载,你可以说类似

a[i] = 7;

(通过引用返回时,您需要确保返回后返回的对象可用:您不应该返回对本地或临时的引用)

•返回为常量值:阻止在赋值表达式的左侧使用该函数。考虑重载运算符+。人们可以这样写:

a + b = c; // This isn't right

将operator +的返回类型设置为“const SomeType”允许按值返回,同时防止表达式在赋值的左侧使用。

以常数值返回也可以防止这样的拼写错误:

if (someFunction() = 2)

当你意味着

if (someFunction() == 2)

如果someFunction()声明为

const int someFunction()

然后上面的if()拼写错误将由编译器捕获。

•作为常量引用返回:此函数调用不能出现在赋值的左侧,并且您希望避免复制(按值返回)。例如。假设我们有一个班级学生,我们想提供一个访问者ID()来获取学生的ID:

class Student
{
    std::string id_;

public:

    const std::string& id() const;
};

const std::string& Student::id()
{
    return id_;
}

考虑id()访问器。这应该声明为const以保证id()成员函数不会修改对象的状态。现在,考虑返回类型。如果返回类型是字符串&然后人们可以写出类似的东西:

Student s;
s.id() = "newId";

这不是我们想要的。

我们可以按值返回,但在这种情况下,通过引用返回更有效。使返回类型为const字符串&另外还可以防止修改id。

答案 1 :(得分:6)

要理解的基本事项是按值返回将创建对象的新副本。通过引用返回将返回对现有对象的引用。注意:就像指针一样,你可以有悬空参考。因此,不要在函数中创建一个对象并返回对该对象的引用 - 它将在函数返回时被销毁,并且它将返回一个悬空引用。

按价值返回

  • 当你有POD(普通旧数据)时
  • 如果要返回对象的副本

按引用方式返回:

  • 如果您有性能原因要避开要返回的对象的副本,并且您了解对象的生命周期
  • 当您必须返回对象的特定实例,并了解对象的生命周期时

Const / Constant引用可帮助您强制执行代码的合同,并帮助用户的编译器查找使用错误。它们不会影响性能。

答案 2 :(得分:1)

返回一个常量值并不是一个非常常见的习惯用法,因为无论如何你只返回一个只有调用者可以拥有的新东西,所以有一个他们无法修改它的情况并不常见。在你的例子中,你不知道他们将要用它做什么,那你为什么要阻止他们修改呢?

请注意,在C ++中,如果您没有说某些内容是引用或指针,那么它就是一个值,因此您将创建它的新副本而不是修改原始对象。如果你来自默认使用引用的其他语言,这可能不是很明显。

返回引用或const引用意味着它实际上是其他地方的另一个对象,因此对它的任何修改都将影响该另一个对象。一个常见的习语可能是暴露一个类的私人成员。

const意味着无论它是什么都无法修改,所以如果你返回一个const引用,就不能在其上调用任何非const方法或修改任何数据成员。

答案 3 :(得分:1)

  • 按引用方式返回

您可以返回对某个值的引用,例如类成员。这样,您就不会创建副本。但是,您不应该返回对堆栈中值的引用,因为这会导致未定义的行为。

#include <iostream>                                                                                                                                          
using namespace  std;


class A{
private: int a;

public:
        A(int num):a(num){}

        //a to the power of 4.
        int& operate(){
                this->a*=this->a;
                this->a*=this->a;
                return this->a;
        }

        //return constant copy of a.
        const int constA(){return this->a;}

        //return copy of a.
        int getA(){return this->a;}
};

int main(){

        A obj(3);
        cout <<"a "<<obj.getA()<<endl;
        int& b=obj.operate(); //obj.operate() returns a reference!

        cout<<"a^4 "<<obj.getA()<<endl;
        b++;
        cout<<"modified by b: "<<obj.getA()<<endl;
        return 0;
}

b和obj.a“指向”相同的值,因此修改b会修改obj.a的值。

$./a.out 
a 3
a^4 81
modified by b: 82
  • 返回一个const值

另一方面,返回const值表示不能修改所述值。应该注意返回的值是一个副本: 例如,

constA()++;

会导致编译错误,因为constA()返回的副本是常量。但这只是一个副本,并不意味着A :: a是常数。

  • 返回const引用

这与返回const值类似,不同之处在于不返回任何副本,而是返回对实际成员的引用。但是,它无法修改。

const int& refA(){return this->a;}

const int& b = obj.refA();
b++;

将导致编译错误。

答案 4 :(得分:1)

const int exampleOne();

返回某个int的const副本。也就是说,您创建了一个可能无法修改的新int。这在大多数情况下并不真正有用,因为您无论如何都在创建副本,因此您通常不关心它是否被修改。那么为什么不直接返回一个常规的int?

对于更复杂的类型可能会有所不同,但修改它们可能会产生不良副作用。 (从概念上讲,假设一个函数返回一个表示文件句柄的对象。如果该句柄是const,则该文件是只读的,否则可以修改。然后在某些情况下,函数返回一个const值是有意义的。但一般来说,返回const值并不常见。

int& exampleTwo();

这个返回对int的引用。这不会影响该值的生命周期,因此在这种情况下会导致未定义的行为:

int& exampleTwo() {
  int x = 42;
  return x;
}

我们将返回对不再存在的值的引用。编译器可能会对此发出警告,但无论如何它都可能会编译。但它毫无意义,迟早会导致时髦的崩溃。但是在其他情况下经常使用它。如果函数是类成员,它可以返回对成员变量的引用,该成员的生存期将持续到对象超出范围,这意味着函数返回值在函数返回时仍然有效。

const int& exampleThree();

与上面大致相同,返回对某个值的引用,而不取得对它的所有权或影响其生命周期。主要区别在于,现在您正在返回对const(不可变)对象的引用。与第一种情况不同,这通常很有用,因为我们不再处理其他人不知道的副本,因此代码的其他部分可能会看到修改。 (你可能有一个非const定义的对象,以及一个允许代码的其他部分以const形式访问它的函数,方法是返回一个const引用。

答案 5 :(得分:0)

没想过,我们可以通过引用返回一个const值,但是我没有看到这样做的价值。 但是,如果你试图将值传递给像这样的函数

,这是有道理的
void func(const int& a);

这样做的好处是告诉编译器不要在内存中复制变量a(这是通过值而不是通过引用传递参数时完成的)。 const在这里是为了避免变量a被修改。

答案 6 :(得分:0)

你的第一个案例:

const int exampleOne();

对于像int这样的简单类型,这几乎不是你想要的,因为const是没有意义的。按值返回意味着副本,您可以自由地分配给非const对象:

int a = exampleOne(); // perfectly valid.

当我看到这一点时,通常是因为编写代码的人试图成为const-correct,这是值得称赞的,但并不完全理解他们所写内容的含义。但是, 的情况下,重载运算符和自定义类型可以产生影响。

一些编译器(较新的GCC,Metrowerks等)使用简单类型警告这样的行为,因此应该避免使用。

答案 7 :(得分:0)

我认为你的问题实际上是两个问题:

  • 返回const有什么含义。
  • 返回引用有什么含义。

为了给你一个更好的答案,我将对这两个概念进行更多解释。

关于const关键字

const关键字表示无法修改对象该变量,例如:

MyObject *o1 = new MyObject;
const MyObject *o2 = o1;
o1->set(...); // Will work and will change the instance variables.
o2->set(...); // Won't compile.

现在,const关键字可用于三个不同的上下文:

  • 确保您不会修改对象的方法的调用者

例如:

void func(const MyObject &o);
void func(const MyObject *o);

在这两种情况下,对对象所做的任何修改都将保留在函数范围之外,这就是为什么使用关键字const我保证调用者不会修改它的实例变量。

  • 确保编译器特定方法不会改变对象

如果你有一个类和一些方法从实例变量中“获取”或“获取”信息而不修改它们,那么即使使用了const关键字,我也应该能够使用它们。例如:

class MyObject
{
    ...
public:
    void setValue(int);
    int getValue() const; // The const at the end is the key
};

void funct(const MyObject &o)
{
    int val = o.getValue(); // Will compile.
    a.setValue(val); // Won't compile.
}
  • 最后,(您的情况)返回一个const值

这意味着无法直接修改或更改返回的对象。例如:

const MyObject func();

void func2()
{
    int val = func()->getValue(); // Will compile.
    func()->setValue(val); // Won't compile.
    MyObject o1 = func(); // Won't compile.
    MyObject o2 = const_cast<MyObject>(func()); // Will compile.
}

有关const关键字的更多信息:C++ Faq Lite - Const Correctness

关于参考

返回或接收引用意味着不会复制该对象。这意味着对值本身所做的任何更改都将反映在函数范围之外。例如:

void swap(int &x, int &y)
{
    int z = x;
    x = y;
    y = z;
}
int a = 2; b = 3;
swap(a, b); // a IS THE SAME AS x inside the swap function

因此,返回参考值意味着可以更改该值,例如:

class Foo
{
public:
    ...
    int &val() { return m_val; }
private:
    int m_val;
};

Foo f;
f.val() = 4; // Will change m_val.

有关参考资料的更多信息:C++ Faq Lite - Reference and value semantics

现在,回答您的问题

const int exampleOne();

表示返回的对象无法通过变量进行更改。返回对象时更有用。

int& exampleTwo();

表示返回的对象与函数内部的对象相同,对该对象所做的任何更改都将反映在函数中。

const int& exampleThree();

表示返回的对象与函数内的对象相同,不能通过该变量修改。