通过构造函数对C++对象进行部分重新初始化

Partial re-initialization of C++ object through constructor

本文关键字:行部 初始化 对象 构造函数 C++      更新时间:2023-10-16

我正在为C++对象的部分重新初始化寻找最佳模式。

对于部分重新初始化,我的意思是一些成员(代码示例中的step_param)需要保留其值,而其他成员(代码实例中的value)则需要重新初始化。

要点:应该避免与构造函数基本相同的init()或reset()成员函数的膨胀和冗余。

到目前为止,我有以下解决方案:

namespace reinit_example
{
    struct reinit_t {} reinit;
    struct stepper_t
    {
        int step_param; // keep parameter
        int value;
        stepper_t()
            : step_param(1)
            , value(step_param)
        {}
        stepper_t( const stepper_t & c, reinit_t )
            :   step_param(c.step_param)
            ,   value(step_param)
        {}
        void step()
        {
            value += step_param;
        }
    };
    void use_cases_1()
    {
        stepper_t c;
        // use c
        c.step();
        // and later reinit
        c = stepper_t(c,reinit);
    }
} // namespace

它还应该与继承和组合配合良好:

namespace reinit_example
{
    struct stepper_2_t : public stepper_t
    {
        int step_param_2; // keep parameter
        int value_2;
    public:
        stepper_2_t()
            : step_param_2(0)
            , value_2(step_param_2)
        {}
        stepper_2_t( const stepper_2_t & cc, reinit_t )
            :   stepper_t(cc)
            ,   step_param_2(cc.step_param_2)
            ,   value_2(step_param+2)
        {}
        void step()
        {
            stepper_t::step();
            value_2 += value + step_param_2;
        }
    };
    struct stepper_comp_t
    {
        stepper_t c;
        stepper_2_t cc;
    public:
        stepper_comp_t()
        {}
        stepper_comp_t( const stepper_comp_t & d, reinit_t )
            :   c(d.c,reinit)
            ,   cc(d.cc,reinit)
        {}
        void step()
        {
            c.step();
            cc.step();
        }
    };
    void use_cases_2()
    {
        stepper_2_t cc;
        // use cc, change config
        cc.step();
        // maybe change config
        cc.step_param = 2;
        // reinit
        cc = stepper_2_t(cc,reinit);
        stepper_comp_t d;
        d = stepper_comp_t(d,reinit);
    }
} // namespace

C++11非静态成员初始化使其更加简单:

#if __has_feature(cxx_nonstatic_member_init)
namespace reinit_example
{
    struct stepper_11_t
    {
        int step_param = 0 ; // keep value
        int value = step_param;
        stepper_11_t()
        {}
        stepper_11_t( const stepper_11_t & c11, reinit_t )
            :   step_param(c11.step_param)
        {}
    };
    void use_cases_3()
    {
        stepper_11_t c11;
        c11 = stepper_11_t(c11,reinit);
    }
} // namespace
#endif

用于测试:

int main()
{
    reinit_example::use_cases_1();
    reinit_example::use_cases_2();
#if  __has_feature(cxx_nonstatic_member_init)
    reinit_example::use_cases_3();
#endif
}

Jerry Coffin提出的解决方案:将参数移动到一个单独的结构中,并将其传递给构造函数以重新命名。

namespace reinit_example
{
    struct stepper_config_t
    {
        struct config_t
        {
            config_t()
                :   step_param(1)
            {}
            int step_param;
            int other_param;
        };
        config_t config;
        int value;
        stepper_config_t()
            : value(config.step_param)
        {}
        stepper_config_t( const config_t & c)
            :   config(c)
            ,   value(c.step_param)
        {}
        void step()
        {
            value += config.step_param;
        }
    };
    void use_cases_4()
    {
        stepper_config_t c;
        // use c
        // and later reinit
        c = stepper_config_t(c.config);
    }
} // namespace

我认为您应该寻找完全不同的设计模式。

例如,"keeper"成员应该组成一个功能齐全的类,而您不想保留的其他成员将被视为该类的上下文(将是另一个类,用于对第一个类执行一些操作)。

这有点类似于轻量级的设计模式。