C++ 与“代理对象更新延迟”相比;避免使用自定义构造和销毁的未命名对象”;

C++ 与“代理对象更新延迟”相比;避免使用自定义构造和销毁的未命名对象”;,c++,c++11,c++14,c++17,C++,C++11,C++14,C++17,我有一个类complex,它具有修改某些内部状态的各种setter。这种内部状态修改可能会很昂贵,所以我不想经常这样做。特别是,如果立即连续调用多个setter,我希望在最后一次setter调用之后只执行一次昂贵的内部状态更新 我已通过代理解决了(或“解决”?)该要求。以下是最简单的工作代码示例: #include <iostream> class complicated { public: class proxy { public: pro

我有一个类
complex
,它具有修改某些内部状态的各种setter。这种内部状态修改可能会很昂贵,所以我不想经常这样做。特别是,如果立即连续调用多个setter,我希望在最后一次setter调用之后只执行一次昂贵的内部状态更新

我已通过代理解决了(或“解决”?)该要求。以下是最简单的工作代码示例:

#include <iostream>

class complicated
{
public:
    class proxy
    {
    public:
        proxy(complicated& tbu) : to_be_updated(&tbu) {
        }

        ~proxy() {
            if (nullptr != to_be_updated) {
                to_be_updated->update_internal_state();
            }
        }

        // If the user uses this operator, disable update-call in the destructor!
        complicated* operator->() {
            auto* ret = to_be_updated; 
            to_be_updated = nullptr;
            return ret;
        }
    private:
        complicated* to_be_updated;
    };

public:
    proxy set_a(int value) {
        std::cout << "set_a" << std::endl;
        a = value;
        return proxy(*this);
    }

    proxy set_b(int value) {
        std::cout << "set_b" << std::endl;
        b = value;
        return proxy(*this);
    }

    proxy set_c(int value) {
        std::cout << "set_c" << std::endl;
        c = value;
        return proxy(*this);
    }

    void update_internal_state() {
        std::cout << "update" << std::endl;
        expensive_to_compute_internal_state = a + b + c;
    }

private:
    int a;
    int b;
    int c;
    int expensive_to_compute_internal_state;
};

int main()
{
    complicated x;
    x.set_a(1);
    std::cout << std::endl;
    x.set_a(1)->set_b(2);
    std::cout << std::endl;
    x.set_a(1)->set_b(2)->set_c(3);
}
#包括
班级复杂
{
公众:
类代理
{
公众:
代理(复杂和待定):待更新(&待定){
}
~proxy(){
如果(nullptr!=待更新){
更新->更新内部状态();
}
}
//如果用户使用此运算符,请禁用析构函数中的更新调用!
复杂*运算符->(){
自动*ret=待更新;
待更新=nullptr;
返回ret;
}
私人:
复杂*待更新;
};
公众:
代理集_a(int值){

std::cout我认为您的解决方案是合法的,但它有一个缺点,即它对代码用户隐藏,更新成本很高,因此人们更可能会写:

x.set_a(1);
x.set_b(2);

我建议将setters设置为私有,并添加一个friend事务类,以便修改对象如下所示:

complicated x;
{
    transaction t(x);
    t.set_a(1);
    t.set_b(2);
    // implicit commit may be also done in destructor
    t.commit();
}

如果
事务
将是修改
复杂
的唯一方法-用户更倾向于在一个事务中调用多个setter。

我在这里看到的危险是,如果您的类有任何不返回代理(或任何公共成员)的方法。如果使用代理的
操作符->
,则禁用更新调用(这会产生
复杂的
),但只有在使用
操作符->
总是会产生另一个代理对象来接管更新任务时,这才是安全的。对于以后修改类的任何人来说,这似乎是一个巨大的陷阱

我认为,如果
complex
能够跟踪在其上创建的活动
proxy
对象的数量,以便最后一个要销毁的
proxy
执行更新调用,那么会更安全。

根据人们选择“错误”方法的论点,您可能会把事情简单一点(特别是从用户的角度):

这样,您将只在需要时执行计算,而不需要像代理对象或显式事务这样的附加扩展

根据您的需要,您可能会将此模式封装在单独的(模板?)类中,可能会接收更新程序对象(lambda?),或者使用纯虚拟的
update
函数来重复更少的代码


旁注:设置一个值可能会使多个缓存值无效–没问题,请将多个脏标志设置为true,然后…

至少没有警告,您能指出出现此警告的正确行吗?您能否不只是手动更新?警告显示在每行中,其中至少调用了一个设置程序,例如
x.set_a(1);
。它指的是返回的临时
代理
对象。如果它正常工作,可能更好地位于?@Aconcagua谢谢你的指针。我不知道CodeReview的存在!StackOverflow和CodeReview之间的界限在哪里?哦,这是一个非常好的观点。谢谢。除此之外,还有关于警告的想法吗惯性导航与制导?
complicated x;
{
    transaction t(x);
    t.set_a(1);
    t.set_b(2);
    // implicit commit may be also done in destructor
    t.commit();
}
class Demo
{
    int m_x
    int m_y; // cached value, result of complex calculation
    bool m_isDirtyY;
public:
    int x() { return m_x; }
    void x(int value) { m_x = value; m_isDirtyY = true; }
    int y()
    {
        if(m_isDirtyY)
        {
            // complex calculations...
            m_y = result;
            m_isDirtyY = false;
        }
        return m_y;
    }
};