通过Indirection示例解释C ++ Mutability

时间:2015-01-04 23:08:31

标签: c++ lazy-evaluation

在C ++编程语言第4版第16.2.9.4节“间接可变性”中有一个使用间接而不是mutable关键字进行延迟评估的示例草图。

struct cache {
    bool valid;
    string rep;
};

class Date {
public:
    // ...
    string string_rep() const;
private:
    cache * c;    // initialize in constructor
    void compute_cache_value() const;
    // ...
};

string Date::string_rep() const {
    if (!c->valid) {
        compute_cache_value();
        c->valid = true;
    }
    return c->rep;
}

Full runnable example

没有太多解释:

  

当只允许小对象的一小部分表示允许更改时,声明成员mutable是最合适的。通过将更改的数据放在单独的对象中并直接访问它,可以更好地处理更复杂的情况。

我正在寻找更完整的解释。特别是,

  • 小小约束是什么?是少量内存还是少量逻辑?
  • 在构造函数中初始化c是否会失败(在一个非常重要的程度上)懒惰?也就是说,它确实可以为你做多少不需要的工作。
  • 为什么c是指针而不是unique_ptr?前面的章节为了证明异常安全性和RAII而付出了一些努力。
  • 如果你打算在构造函数中分配和初始化mutable cache c,为什么不只有一个c成员呢?

换句话说,这是一个真实的模式还是一个人为的例子来展示间接与持久性?

2 个答案:

答案 0 :(得分:4)

“小小约束”不是一个真正的约束,只是提示如何编写代码(它与使用过的内存没什么关系)。如果你有一个有30个成员的类,其中20个是可变的,那么将它分成两个类(一个用于可变部分,一个用于其余部分)可能更有意义。

为什么它不是一个聪明的指针:不知道,但可能是一本太累的书作者:p

为什么它是一个指针:你是对的,没有必要。制作一个没有任何指针的可变缓存对象也会起作用,如果不需要任何类似指针的方式(比如从外部获取现有对象),指针只会增加另一种制作错误的可能性。

答案 1 :(得分:1)

警告:以下文字对战场程序员都没有任何实际价值,而不是程序员 - 哲学家或普通哲学家。另外,我是Bjarne Stroustrup的忠实粉丝,我的观点可能有偏见。不幸的是,StackOverflow格式不适合书籍讨论。功能

另外,我们正在讨论关于constness-mutability的尴尬问题,我们应该对编译器和类用户撒谎。而且没有单一的正确意见。我准备被评论和投票;)

简而言之:

  • 你可能没有理解懒惰的初始化究竟意味着什么(可能是因为书中没有正确选择这个术语)与RAII一样,我们知道Bjarne在选择术语方面表现不佳; )
  • 在编写有关编程的书时,几乎没有必要做出决定。因此,有些问题归结为“如何写一本书?"而不是"如何编写生产代码?"。

长期:

  1.   

    小小约束是什么?它是少量的内存还是一个   少量的逻辑?

    我再次引用Bjarne:

      

    当只允许小型对象的表示的一小部分时,声明成员可变最合适

    我认为他的意思是"少数数据成员"这里。通过将数据成员分组到单独的类中进行重构通常是一个很好的建议。 "适当"之间的比例是多少?和"小"?你自己决定(给出一个真正的问题,一个分析器工具和内存/速度/ battery_life / money / client_happiness等约束。)

  2.   

    没有在构造函数中初始化c失败(到一个非常重要的程度)懒惰?也就是说,它确实可以帮助你做很多事情。

    好吧,通过延迟初始化,我们指的是每次用户请求字符串时非计算正确的字符串值(即compute_cache_value()),但仅在真正需要时。不是用空字符串初始化,对吧? std::string无论如何都在构造时初始化为空字符串)

    第16.2.9.3和16.2.9.4章中Bjarne的代码中没有任何构造函数!并且您也不会在代码中的构造函数中计算字符串,而是使用空字符串文字初始化它。所有计算都推迟到最后一刻。所以,懒惰的初始化对我来说非常适合。

    进一步过早优化,如果你想要真正的延迟初始化,你可能会在构造函数中保留cache*指针未初始化,并分配on第一次Date::string_rep()来电。如果您的缓存很大并且用户从不需要它,那么这将是安全的堆。这样你就可以在cache构造函数中包装计算,这使得懒惰的评估变为非常懒惰的初始化

  3.   

    为什么c是裸指针而不是unique_ptr?该   前几章为了证明异常而付出了一些努力   安全和RAII。

    In" C ++编程语言,第4版"第17章介绍了智能指针,我们讨论的是第16章。此外,描述可变性并不重要,只要你在析构函数中设法delete就没有任何优势。另一件事是作者本章将解释为什么你可以改变smart_ptr cache所拥有的资源,smart_ptr方法中只有常量const对象,这将引入描述运算符重载(大多数高级 Java和Python 程序员会在那个地方丢掉这本书;)。

    除此之外,这一般是一个难题。首先,Bjarne Stroustrup的书籍主要被视为教材或指南。那么,在教新手时,我们应该先进入智能指针还是先教导原始指针?我们应该立即使用标准库还是将其留给最后几章? C ++ 14从一开始就是" C +"第一?谁知道?还有一个问题被称为"过度使用智能指针",尤其是shared_ptr

  4.   

    如果你打算在构造函数中分配和初始化c,为什么不只有一个可变的缓存c成员呢?

    那是16.2.9.3中描述的内容,对吧?

    在这里添加一个间接级别是替代解决方案(显示"没有适用于所有目的的通用解决方案")并演示这个惊人的引用:

    <击>
    <击>   

    计算机科学中的所有问题都可以通过另一层次的间接解决,除了间接层太多的问题。    - David J. Wheeler   

    没有。当用户 xan 澄清时,16.9.3是关于多个可变成员,而单个mutable struct会提供一些关注点分离。

  5. 希望你喜欢阅读!