我正在学习C ++,但仍然对此感到困惑。在C++中返回值作为常量、引用和常量引用有什么影响?例如:
const int exampleOne();
int& exampleTwo();
const int& exampleThree();
我正在学习C ++,但仍然对此感到困惑。在C++中返回值作为常量、引用和常量引用有什么影响?例如:
const int exampleOne();
int& exampleTwo();
const int& exampleThree();
a[i] = 7;
当使用引用返回值时,需要确保在返回后该对象仍然可用:不应返回对局部变量或临时变量的引用。
• 以常量值形式返回:防止函数在赋值表达式的左侧使用。考虑重载运算符+。可以这样写:
a + b = c; // This isn't right
operator+ 的返回类型为 "const SomeType",允许通过值返回并同时防止在赋值的左侧使用表达式。
以常量值返回还可以避免输入错误,例如:
if (someFunction() = 2)
当您想表达时
if (someFunction() == 2)
如果将someFunction()声明为
const int someFunction()
如果有一个if()的错别字,编译器会发现并提示错误。
• 作为常量引用返回:该函数调用不能出现在赋值语句的左侧,并且您希望避免复制(通过值返回)。例如,假设我们有一个名为Student的类,并且我们想提供一个访问器id()来获取学生的ID:
class Student
{
std::string id_;
public:
const std::string& id() const;
};
const std::string& Student::id()
{
return id_;
}
考虑id()访问器。应该声明为const,以确保id()成员函数不会修改对象的状态。现在考虑返回类型。如果返回类型是string&,那么可以编写类似以下内容的代码:
Student s;
s.id() = "newId";
这并不是我们想要的。
我们本可以返回值,但在这种情况下,通过引用返回更加高效。将返回类型设置为const string&还可以防止id被修改。
您可以返回一个指向某个值的引用,比如类成员。这样就不会创建副本。但是,在栈中返回值的引用是不明确的行为,应避免这种情况。
#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
另一方面,返回一个常量值表示该值不能被修改。需要注意的是,返回的值是一份副本: 例如,
constA()++;
这会导致编译错误,因为constA()返回的副本是常量。但这只是一个副本,它并不意味着A::a是常量。
这类似于返回一个const值,除了不返回副本,而是返回对实际成员的引用。然而,它不能被修改。
const int& refA(){return this->a;}
const int& b = obj.refA();
b++;
会导致编译错误。
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方式访问它的函数。
返回一个常量值并不是一个很常见的习惯用法,因为你返回的是一个新的东西,只有调用者才能拥有它,所以很少出现他们不能修改它的情况。在你的例子中,你不知道他们会用它做什么,那么为什么要阻止他们修改呢?
请注意,在C++中,如果你没有声明某个东西是引用或指针,它就是一个值,所以你将创建一个新的副本而不是修改原始对象。如果你来自其他默认使用引用的语言,这可能不是很明显。
返回一个引用或常量引用意味着它实际上是另一个对象,因此对它的任何修改都将影响到该其他对象。一个常见的惯用语法可能是暴露类的私有成员。
const
表示无论什么情况下都不能修改它,因此如果你返回一个const引用,你不能调用任何非const方法或修改任何数据成员。
从来没有想过,我们可以通过引用返回一个const值,我不认为这样做有价值。 但是,如果您尝试像这样将一个值传递给函数,那么这是有意义的
void func(const int& a);
这样做的好处是告诉编译器不要在内存中复制变量a(当您按值而不是按引用传递参数时会执行此操作)。const是为了避免修改变量a。
你的第一个案例:
const int exampleOne();
对于像 int 这样的简单类型,这几乎永远不是你想要的,因为 const 是无意义的。按值返回意味着复制,你可以自由地将其分配给非 const 对象:
int a = exampleOne(); // perfectly valid.
当我看到这个时,通常是因为编写代码的人试图保持const正确性,这是值得称赞的,但他们并没有完全理解自己所写的内容的含义。然而,在重载运算符和自定义类型的情况下,确实存在一些情况会有所不同。
一些编译器(新版GCC、Metrowerks等)会对简单类型的此类行为发出警告,因此应该避免使用。
我认为你的问题实际上包含两个问题:
为了给你一个更好的答案,我将对这两个概念进行一些解释。
关于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.
const int exampleOne();
意味着通过变量返回的对象不能被更改。在返回对象时更有用。
int& exampleTwo();
这意味着返回的对象与函数内部的对象相同,对该对象所做的任何更改都将在函数内部反映。
const int& exampleThree();
意味着返回的对象与函数内部的对象相同,不能通过该变量进行修改。