【问题标题】:Const Struct&构造结构&
【发布时间】:2010-09-08 14:41:58
【问题描述】:

我很难弄清楚 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() 中的代码会编译吗?

非常感谢。

【问题讨论】:

    标签: c++ constants


    【解决方案1】:

    unchangeable.AccessWidgets():

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

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

    【讨论】:

    • 没有在 WidgetHolder 中创建新的小部件;成员是引用,但构造函数参数是副本。
    • 帕特里克你错了。构造函数正在创建新的小部件,然后引用是对这些新小部件的引用。他没有得到对他原始小部件的引用。
    【解决方案2】:

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

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

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

    【讨论】:

    • 另外,如果您明确保证这些对象的生命周期和所有权,则传递指针(而不是引用)也可以。如果传递引用,WidgetHolder 就无法“拥有”对象。
    【解决方案3】:

    这是可以编译的,因为尽管 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){}
    

    在你这样做之后,它不会编译,我把它作为练习留给读者解决其余的解决方案。

    【讨论】:

      【解决方案4】:

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

      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;

      【讨论】:

        【解决方案5】:

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

        Flame 的回答是非常错误的。他的 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) {} /* ... */ };

        【讨论】:

          【解决方案6】:

          最初的查询是如果包含类是 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 成为持有者的私有成员,会更容易,并为每个 Widget 提供一个 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;
          ...
          };
          

          【讨论】:

            猜你喜欢
            • 2022-01-15
            • 1970-01-01
            • 2017-07-25
            • 2010-11-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多