8

我很难弄清楚 const 在特定情况下的确切应用方式。这是我的代码:

struct Widget
{
    Widget():x(0), y(0), z(0){}

    int x, y, z;
};

struct WidgetHolder //Just a simple struct to hold four Widgets.
{
    WidgetHolder(Widget a, Widget b, Widget c, Widget d): A(a), B(b), C(c), D(d){}

    Widget& A;
    Widget& B;
    Widget& C;
    Widget& D;
};

class Test //This class uses four widgets internally, and must provide access to them externally.
{
    public:
        const WidgetHolder AccessWidgets() const
        {
            //This should return our four widgets, but I don't want anyone messing with them.
            return WidgetHolder(A, B, C, D);
        }

        WidgetHolder AccessWidgets()
        {
            //This should return our four widgets, I don't care if they get changed.
            return WidgetHolder(A, B, C, D);
        }

    private:
        Widget A, B, C, D;
};

int main()
{
    const Test unchangeable;

    unchangeable.AccessWidgets().A.x = 1; //Why does this compile, shouldn't the Widget& be const?
}

基本上,我有一个名为 test 的类。它在内部使用了四个小部件,我需要它来返回这些小部件,但如果 test 被声明为 const,我希望小部件也返回 const。

有人可以向我解释为什么 main() 中的代码可以编译吗?

非常感谢你。

4

6 回答 6

7

您需要创建一个专门用于保存 const Widget& 对象的新类型。IE:


struct ConstWidgetHolder
{
    ConstWidgetHolder(const Widget &a, const Widget &b, const Widget &c, const Widget &d): A(a), B(b), C(c), D(d){}

    const Widget& A;
    const Widget& B;
    const Widget& C;
    const Widget& D;
};

class Test
{
public:
    ConstWidgetHolder AccessWidgets() const
    {
        return ConstWidgetHolder(A, B, C, D);
    }

您现在将收到以下错误(在 gcc 4.3 中):

widget.cc:在函数“int main()”中:
widget.cc:51:错误:在只读结构中分配数据成员“Widget::x”

在带有迭代器的标准库中使用了一个类似的习惯用法,即:


class vector {
    iterator begin();
    const_iterator begin() const;

于 2008-09-11T19:40:07.137 回答
3

unchangeable.AccessWidgets():

此时,您正在创建一个 WidgetHolder 类型的新对象。此对象不受 const 保护。

您还在 WidgetHolder 中创建新的小部件,而不是对 Wdiget 的引用。

于 2008-09-11T19:09:14.857 回答
3

WidgetHolder将持有无效的引用(指针)。您将堆栈上的对象传递给构造函数,然后保存对其(临时)地址的引用。这是保证打破。

您应该只将引用分配给与引用本身具有相同(或更长)生命周期的对象。

如果必须持有引用,则将引用传递给构造函数。更好的是,根本不要持有参考资料,而只是制作副本。

于 2008-09-11T19:11:15.007 回答
2

这是可以编译的,因为尽管 WidgetHolder 是一个 const 对象,但这种 const 特性不会自动应用于指向(引用)WidgetHolder 的对象。在机器级别考虑它 - 如果 WidgetHolder 对象本身保存在只读内存中,您仍然可以写入 WidgetHolder 指向的内容。

问题似乎出在这一行:

WidgetHolder(Widget a, Widget b, Widget c, Widget d): A(a), B(b), C(c), D(d){}

正如弗兰克所说,您在 WidgetHolder 类中的引用将在构造函数返回后保存无效引用。因此,您应该将其更改为:

WidgetHolder(Widget &a, Widget &b, Widget &c, Widget &d): A(a), B(b), C(c), D(d){}

完成此操作后,它将无法编译,我将其作为练习留给读者解决其余的解决方案。

于 2008-09-11T19:14:59.427 回答
0

编辑:他删除了他的答案,让我看起来有点愚蠢:)

火焰的答案是危险的错误。他的 WidgetHolder 在构造函数中引用了一个值对象。构造函数返回后,按值传递的对象将被销毁,因此您将持有对已销毁对象的引用。

使用他的代码的一个非常简单的示例应用程序清楚地表明了这一点:

#include <iostream>

class Widget
{
    int x;
public:
    Widget(int inX) : x(inX){}
    ~Widget() {
    std::cout << "widget " << static_cast< void*>(this) << " destroyed" << std::endl;
     }
};

struct WidgetHolder
{
    Widget& A;

public:
    WidgetHolder(Widget a): A(a) {}

    const Widget& a() const {
    std::cout << "widget " << static_cast< void*>(&A) << " used" << std::endl;
    return A;
}

};

int main(char** argv, int argc)
{
Widget test(7);
WidgetHolder  holder(test);
Widget const & test2 = holder.a();

return 0;
} 

输出将类似于

小部件 0xbffff7f8 被破坏
小部件 0xbffff7f8 已使用
小部件 0xbffff7f4 被破坏

为了避免这种情况,WidgetHolder 构造函数应该引用它想要存储为引用的变量。

结构 WidgetHolder
{
    小部件&A;

上市:
    WidgetHolder(Widget & a): A(a) {}

  /* ... */

};
于 2008-09-12T09:08:09.097 回答
0

最初的查询是如果包含类是 const,如何将 WidgetHolder 作为 const 返回。C++ 使用 const 作为函数签名的一部分,因此您可以拥有同一函数的 const 和非 const 版本。当实例为非 const 时调用 none const ,当实例为 const 时调用 const 。因此,一种解决方案是通过函数而不是直接访问小部件持有者中的小部件。我在下面创建了一个更简单的示例,我相信它可以回答原始问题。

#include <stdio.h>

class Test
{
public:
  Test(int v){m_v = v;}
 ~Test(){printf("Destruct value = %d\n",m_v);}

 int& GetV(){printf ("None Const returning %d\n",m_v); return m_v;  }

 const int& GetV() const { printf("Const returning %d\n",m_v); return m_v;}
private:
  int m_v;
};

void main()
{
  // A none const object (or reference) calls the none const functions
  // in preference to the const
  Test one(10);
  int& x = one.GetV();
  // We can change the member variable via the reference
  x = 12;

  const Test two(20);
  // This will call the const version  
  two.GetV();

  // So the below line will not compile
  // int& xx = two.GetV();

  // Where as this will compile
  const int& xx = two.GetV();

  // And then the below line will not compile
  // xx = 3;

}

就原始代码而言,我认为将 WidgetHolder 作为 Test 类的成员,然后返回对它的 const 或无 const 引用,并使 Widgets 成为持有者的私有成员,并提供一个每个小部件的 const 和无 const 访问器。

class WidgetHolder {
...

Widget& GetA();
const Widget& GetA() const;
...
};

然后在主课上

class Test {
...
WigetHolder& AccessWidgets() { return m_Widgets;}
const WidgetHolder&AcessWidgets() const { return m_Widgets;}

private:
  WidgetHolder m_Widgets;
...
};
于 2008-09-15T13:01:28.810 回答