我有一些关于修复类成员变量引用的问题。
我有以下代码:
#include <stdint.h>
#include <string>
#include <iostream>
#include <set>
void
PrintSet (const std::string & str, const std::set<uint32_t> & to_print)
{
std::cout << str << " (" << to_print.size () << "): ";
for (std::set<uint32_t>::const_iterator it = to_print.begin ();
it != to_print.end (); ++it)
{
std::cout << *it << " ";
}
std::cout << "\n";
}
class Test
{
private:
std::set<uint32_t> m_values;
public:
Test () : m_values () { }
void
SetValues (const std::set<uint32_t> & values)
{
m_values = values;
}
const std::set<uint32_t> &
GetValues () const
{
return m_values;
}
std::set<uint32_t> &
GetValues ()
{
return m_values;
}
void
Print () const
{
PrintSet ("TestInst", m_values);
}
};
我注意到如果我这样做:
std::set<uint32_t> returned = test.GetValues ();
变量returned
获取副本,而不是参考,为什么?
const std::set<uint32_t> & GetValues () const
函数必须是否包含双const
(返回值上的那个和函数名后面的那个)?
编辑:其他问题:
知道std::set<uint32_t> returned = test.GetValues ();
创建副本。如果我这样做:
test.GetValues ().size ();
为了调用size ()
函数(或返回对象的任何其他成员),是暂时创建的副本还是使用返回的引用解析所有副本?
有三个函数,按值返回,通过const引用和引用是不是很糟糕?
// Return by value
std::set<uint32_t>
GetValues () const
{
return m_values;
}
// Return by const-reference
const std::set<uint32_t> &
GetValues () const
{
return m_values;
}
// Return by reference
std::set<uint32_t> &
GetValues ()
{
return m_values;
}
答案 0 :(得分:3)
返回的变量是一个副本,而不是一个引用,为什么?
当您使用引用初始化其类型不是引用的值时,您将获得一个副本,例如
#include <cassert>
int main() {
int a = 1;
const int& a_ref = a;
int b = a_ref;
assert(&b != &a);
}
const std::set<uint32_t> & GetValues () const
函数是否必须包含双const
第二个const作为成员函数限定符应用,这意味着当类的调用实例是const限定时,可以调用该方法。例如
Test test;
test.GetValues(); // 1
const Test& test_ref = test;
test_ref.GetValues(); // 2
此处1
将调用非const版本,而2
将调用使用const
此外,const
限定方法不允许您返回非const引用它自己的值,因此您必须返回const
对您的成员变量m_values
的引用。
因此,如果您包含第二个const
,那么您必须包含第一个const
,但是如果您只是将返回类型设为const
引用,那么您不需要方法const
。例如
const std::set<uint32_t>& GetValues() // 1
std::set<uint32_t>& GetValues() { return m_values; } // 2
此处允许1
,但不允许2
。
如果您感到好奇,会发生这种情况的原因是因为隐式this
指针const
有资格成为const
限定方法中const
的指针。
为了调用size()函数(或返回对象的任何其他成员),是临时创建的副本还是使用返回的引用解析所有副本?
将在引用上调用size()
方法!测试这样的事情的最好方法是在快速测试用例https://wandbox.org/permlink/KGSOXDkQESc8ENPW中尝试一下(注意我已经使测试比仅仅用于演示而复杂一点)
有三个函数,按值返回,通过const引用和引用是不是很糟糕?
如果您将用户暴露给可变引用,那么让它们进行复制(这就是为什么使用by-value)方法的最佳方法是让它们通过初始化非引用来自行复制带有返回引用的限定set
(就像您最初那样)
同样在您的代码中,以下两种方法之间存在歧义
std::set<uint32_t> GetValues() const
const std::set<uint32_t>& GetValues () const
因为所有不同的是返回类型and you cannot overload a function off the return type。
答案 1 :(得分:3)
变量returned
是一个单独的对象,必须有自己的值。
因此在本声明中
std::set<uint32_t> returned = test.GetValues ();
函数返回的引用引用的值被复制到对象returned
。
如果代替对象,则声明引用,例如
std::set<uint32_t> &returned = test.GetValues ();
然后在这种情况下,引用returned
将引用类对象的原始对象,并由函数返回的引用初始化。
至于第二个问题,则返回的类型不参与重载的函数解析。
例如,如果您将删除第二个const限定符
const std::set<uint32_t> & GetValues ();
std::set<uint32_t> & GetValues ();
然后这两个函数之间存在歧义,就像这样的调用
std::set<uint32_t> returned = test.GetValues ();