我正在学习C ++,我仍然对此感到困惑。在C ++中将值作为常量,引用和常量引用返回有什么含义?例如:
const int exampleOne();
int& exampleTwo();
const int& exampleThree();
答案 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)
要理解的基本事项是按值返回将创建对象的新副本。通过引用返回将返回对现有对象的引用。注意:就像指针一样,你可以有悬空参考。因此,不要在函数中创建一个对象并返回对该对象的引用 - 它将在函数返回时被销毁,并且它将返回一个悬空引用。
按价值返回
按引用方式返回:
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值表示不能修改所述值。应该注意返回的值是一个副本: 例如,
constA()++;
会导致编译错误,因为constA()返回的副本是常量。但这只是一个副本,并不意味着A :: a是常数。
这与返回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关键字表示无法修改对象到该变量,例如:
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 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();
表示返回的对象与函数内的对象相同,不能通过该变量修改。