【问题标题】:Is there a way to make a constant getter with references?有没有办法用引用制作一个恒定的吸气剂?
【发布时间】:2013-01-26 04:29:59
【问题描述】:

考虑以下模式:

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*),但似乎有一个共识(而且理所当然地)认为引用是可取的,因为它们隐藏了指针的复杂性。

有什么办法吗?如果没有,我将恢复为指针。

【问题讨论】:

  • 我认为您没有 C++11 实现,因此可以将 Child c; 成员变量标记为 mutable 并将 getter 更改为 Child&amp; getChild() const { return c; }
  • @WhozCraig 这在 C++11 中是禁止/不可能的吗?
  • 它很可能(很小的机会)是 UB,我必须检查一下,但我记得可变成员的全部意义在于即使在 const 环境中也允许它们不热。很确定做我上面发布的事情是合法的。我当然希望这不是不可能的,因为如果是这样的话,我的工具链就很糟糕了。
  • @WhozCraig:这不是 C++11 的新特性。
  • @BenjaminLindley mutable memvars 在 11 点之前就已经存在了? Jeezors 我需要多出去走走。

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


【解决方案1】:

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

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

但是,这只是当您尝试返回一个成员变量时的问题,该成员变量是类本身的一部分(它本身不是指针)。因此,正如您已经建议的那样,将 Child 设为指针就可以了。但是将非常量指针返回到非指针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-&gt;c 的类型为const Child &amp;。返回 Child &amp; 违反了 const-ness。

getter 本身不会修改对象,但它允许规避调用者代码中的 const-ness,如上面的代码所示。

【讨论】:

  • 好答案。理解的中心点来自 c,作为“值类型”实际上是 consted Parent 内存块的一部分,而不仅仅是一个引用。指针似乎是合乎逻辑的选择。或者你对我的需要有什么建议吗?
  • 看来你实现了一个树形数据结构。通常,您使用指针或引用来实现它们。如果引用在其整个生命周期中都指向同一个对象,则可以使用引用(您不能“重新安装”引用,但可以使用指针)。在这两种方式中,指针/引用都是对象的一部分,而不是指向的对象,因此返回这样的(可修改的)指针/引用是有效的。
【解决方案2】:

要求看起来有冲突,因为如果 Parent 不能被修改,那么就意味着即使 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");
}

【讨论】:

    猜你喜欢
    • 2020-09-30
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 2021-04-02
    • 2023-02-02
    • 2020-05-11
    • 2021-02-27
    • 2021-04-14
    相关资源
    最近更新 更多