【问题标题】:C++: Why doesn't this sync() work in this Composition pattern?C++:为什么这个 sync() 在这个合成模式中不起作用?
【发布时间】:2012-05-26 23:11:10
【问题描述】:

我正在尝试通过使用类似于组合模式的东西来构建一个进度条类,该类可以包含任意数量的子进度条。

假设我有这门课pbar:

class pbar
{
    public:
        pbar(const int w) { width = w; } // already sets the
        ~pbar() {}

         void setwidth(const int w) { width = w; } // set the width to w
         void show() const;
         void sync();

         void add(const pbar bar)
         {
              // add's a subbar
              subbars.pushback(bar);
         }

     private:
         std::vector<pbar> subbars; // the sub-process progressbars
         int width;                 // onscreen width of the pbar
};

如您所见,pbar 有两个成员:宽度和子进度条(它们本身就是pbars)。我一直在尝试实现一个sync 函数,该函数将subbarspbars 的所有宽度更改为与调用它的pbar 的宽度相匹配:

void pbar::sync()
{
    for ( pbar bar : subbars )
    {
         bar.setwidth(width);  // first set the width of the subbar
         bar.sync();           // secondly make it sync up it's subbars
    }
}

但这似乎不起作用。我试过使用这个测试程序:

int main()
{
    pbar a(1);
    pbar b(2);
    pbar c(3);
    pbar d(4);

    c.add(d);
    b.add(c);
    a.add(b);

    a.show();
    std::cout << "syncing" << std::endl;
    a.sync();
    a.show();
}

show 函数定义为:

void pbar::show() const
{
    std::cout << w << std::endl;
    for ( pbar bar : subbars )
    {
         bar.show();
    }
}

预期的输出是:

1
1
1
1

其实是这样的:

1
2
3
4

奇怪的是 show() 函数确实正确地迭代到所有子栏,但看起来 sync() 没有(事实上,使用 cout 我已经确认实际上 确实,但似乎没有效果)。

我的代码有什么问题?这不是使用 c++0x 类型的 for 循环,因为我尝试过使用较旧的迭代器循环。我找不到我犯的错误。我认为这与我在sync 中使用setwidth 时更改了错误的pbars 有关。

免责声明:这实际上是一个更大项目的一部分,并且该类比此处显示的要复杂得多,但我已经设法使用上面的代码重现了不需要的行为(顺便说一句,这不是复制粘贴,可能包含错字)

【问题讨论】:

    标签: c++ algorithm composition


    【解决方案1】:

    您应该存储指向子pbars 的指针。在当前情况下,您只是存储子pbars 的副本。因此,尽管它们(内部副本)发生了变化,但外部对象并没有被修改。

    【讨论】:

    • 所以不使用指针就无法更改已经在向量中的对象!?哇,不知道。一定是因为最近几周使用了太多指针。
    • @romeovs 不,您的代码确实更改了您存储的子栏。唯一的问题是,正如 vizier 已经指出的那样,您要存储原始子栏的 副本(在 add() 函数中)。
    • @Gnosophilon:实际上我不这么认为。我同意 Blckknght 的观点,他正在更改迭代器变量,它是存储子栏的副本。此外,打印的宽度是存储子栏的宽度,而不是bcd 实例。
    • @Gnosophilon 实际上再次查看他的代码,即使修改了内部副本,他的a.show(); 也会显示内部修改后的副本,不是吗?应该显示“1 1 1 1”
    • @VictorT.:show() 函数在subbars 向量中显示内部副本,但这些内部副本永远不会被修改,因为循环变量是这些元素的另一个副本(因此对其所做的更改将被丢弃)。
    【解决方案2】:

    您遇到的问题是您在 sync() 方法的循环中使用了局部变量“bar”。那就是制作每个子栏的副本,并操纵副本而不是原始版本(保留在向量中)。这就是为什么当您稍后调用 show() 方法时没有看到更改“坚持”的原因。

    您可以通过使用引用而不是常规变量来解决此问题。试试:

    for ( pbar &bar : subbars )
    {
        ...
    }
    

    您可能希望在 addSubBar() 方法中进行类似的更改,因为在将另一个副本保存到向量中之前,您还要复制您传入的值。您可以通过将其参数设为引用来跳过一个副本。避免第二个副本需要更加小心地处理内存(我将留待另一个问题)。

    【讨论】:

      【解决方案3】:

      不是使用c++0x类型的for循环

      实际上,取决于您真正想要做什么,它可能只是您正在使用的基于范围的for 循环。

      正如问题中所发布的,subbars 向量存储添加到其中的对象的副本 - 这可能是也可能不是您想要的。让我们假设它是你想要的。您现在在 pbar::sync() 中拥有的基于范围的 for 循环:

      for ( pbar bar : subbars )
      {
          // ...
      }
      

      迭代subbars 向量,但在这种情况下,bar 变量本身就是subbars 向量中每个元素的副本。因此,在 for 循环的每次迭代之后,您对该变量所做的任何更改都会丢失。

      但是,如果您像这样更改基于范围的 for 循环:

      for ( pbar& bar : subbars ) // note the `&`
      {
          // ...
      }
      

      现在,bar 是对 subbars 向量中对象的引用,对其所做的更改将“坚持”。

      但是请记住,由于subbars 包含添加到其中的对象的副本,因此这些更改不会传播到添加的原始对象。这是否是你想要的取决于你想要什么。如果您希望更改一直传播到原件,那么您需要将指针(或智能指针)存储到原件而不是副本,就像mentioned in visier's answer 一样。

      【讨论】:

      • 这实际上是寻找的行为!
      【解决方案4】:

      您应该存储pbars 的引用而不是实例副本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-30
        • 1970-01-01
        • 2018-11-05
        • 2020-07-01
        相关资源
        最近更新 更多