有没有办法使用引用来制作一个常量的getter?

时间:2013-01-26 04:30:00

标签: c++ pointers reference pass-by-reference

考虑以下模式:

class Child
{
public:
    char Foo;

    Child(char foo)
    {
        Foo = foo;
    }
};

class Parent
{
public:
    Child c;

    Parent() : c('A') { }

    const Child& GetChild() const
    {
        return c;
    }
};

// Scenario 1: Works, but useless
int main()
{
    Parent p = Parent();
    Child c = p.GetChild();
    c.Foo = 'B';
    cout << p.c.Foo << endl; // A, wrong
    cout << c.Foo << endl; // B, good
    system("PAUSE");
}

// Scenario 2: Doesn't compile, of course
int main()
{
    Parent p = Parent();
    Child& c = p.GetChild(); // Error
    c.Foo = 'B';
    cout << p.c.Foo << endl; // A, good
    cout << c.Foo << endl; // B, good
    system("PAUSE");
}

规范如下:

  • 必须将getter定义为const(因为它不会修改Parent)
  • getter提供的引用必须修改基础值

问题在于:

  • 如果getter本身是const,那么C ++要求getter的返回值为const(为什么?)
  • C ++禁止将const值分配给引用(逻辑上)

使用指针(使访问器返回Child*)很容易实现这一点,但似乎有一个共识(并且理所当然地),建议引用是可取的,因为它们隐藏了指针的复杂性。 / p>

有什么办法吗?如果没有,我只会回到指针。

2 个答案:

答案 0 :(得分:2)

如果返回的引用不是const,则调用者可以修改该对象,即使它在自己的上下文中是const:

const Parent p = ...
Child & child = p.GetChild(); // valid const call, invalid return type

但是,当您尝试返回属于类本身(它本身不是指针)的成员变量时,这只是问题。正如你已经建议的那样,让Child成为指针就行了。但是将非const指针返回到非指针Child会导致同样的问题......

为了更好地说明问题,请考虑以下内存布局说明:

Parent:  [Child]

Child:   [char ]

因此,父级包含Child个对象,因此修改Parent实例会修改Child个实例,而修改Parent::c会修改Parent

所以你不能返回指针或对非const对象的引用(this指针指向const成员函数):

Child& GetChild() const
{
    return c;    // compiler will complain here
}

等于:

Child& GetChild() const
{
    return this->c;   // compiler will complain here
}

其中this的类型为const Parent *,这意味着this->c的类型为const Child &。返回Child &会违反常量。

getter本身不会修改对象,但它允许绕过调用者代码中的常量,如上面的代码所示。

答案 1 :(得分:0)

要求看起来有冲突,因为如果不能修改Parent,那么即使Child成员也不应该被修改。

但如果您的规范被放宽并且可以修改Child成员对象,那么可能的解决方案是使用const_cast。

class Child
{
public:
    char Foo;

    Child(char foo)
    {
        Foo = foo;
    }
};

class Parent
{
public:
    Child c;

    Parent() : c('a') { }

    const Child& GetChild() const
    {
        return c;
    }
};

// Scenario 2: Doesn't compile, of course
int main()
{
    Parent p = Parent();
    Child& c = const_cast<Child &>(p.GetChild()); // No more Error
    c.Foo = 'B';
    std::cout << p.c.Foo << std::endl; // A, good
    std::cout << c.Foo << std::endl; // B, good
    system("PAUSE");
}