C++ &引用;“前进不可破”;访问器类模板[C+;+;]

C++ &引用;“前进不可破”;访问器类模板[C+;+;],c++,templates,accessor,getter-setter,C++,Templates,Accessor,Getter Setter,除非我完全弄错了,否则getter/setter模式是用于两件事的常见模式: 通过只提供getVariable方法(或者,更罕见的是,通过只提供setVariable方法,仅可修改),使私有变量可以使用,但决不能修改 为了确保在将来,如果您碰巧遇到一个问题,而一个好的解决方案是在变量进入和/或离开类之前处理它,那么您可以通过在getter和setter方法上使用实际实现来处理变量,而不是简单地返回或设置值。这样,更改不会传播到代码的其余部分 问题#1:我是否遗漏了访问器的使用,或者我的假设是否有

除非我完全弄错了,否则getter/setter模式是用于两件事的常见模式:

  • 通过只提供
    getVariable
    方法(或者,更罕见的是,通过只提供
    setVariable
    方法,仅可修改),使私有变量可以使用,但决不能修改
  • 为了确保在将来,如果您碰巧遇到一个问题,而一个好的解决方案是在变量进入和/或离开类之前处理它,那么您可以通过在getter和setter方法上使用实际实现来处理变量,而不是简单地返回或设置值。这样,更改不会传播到代码的其余部分
  • 问题#1:我是否遗漏了访问器的使用,或者我的假设是否有误?我不确定我在这些问题上是否正确

    问题#2:有什么样的模板优点可以让我不必为我的成员变量编写访问器吗?我没找到

    问题#3:下面的类模板是实现getter而不必实际编写accesor的好方法吗

    template <class T>
    struct TemplateParameterIndirection // This hack works for MinGW's GCC 4.4.1, dunno others
    {
        typedef T Type;
    };
    
    template <typename T,class Owner>
    class Getter
    {
    public:
        friend class TemplateParameterIndirection<Owner>::Type; // Befriends template parameter
    
        template <typename ... Args>
        Getter(Args args) : value(args ...) {} // Uses C++0x
    
        T get() { return value; }
    
    protected:
        T value;
    };
    
    class Window
    {
    public:
        Getter<uint32_t,Window> width;
        Getter<uint32_t,Window> height;
    
        void resize(uint32_t width,uint32_t height)
        {
            // do actual window resizing logic
    
            width.value = width; // access permitted: Getter befriends Window
            height.value = height; // same here
        }
    };
    
    void someExternalFunction()
    {
        Window win;
    
        win.resize(640,480); // Ok: public method
    
        // This works: Getter::get() is public
        std::cout << "Current window size: " << win.width.get() << 'x' << win.height.get() << ".\n";
    
        // This doesn't work: Getter::value is private
        win.width.value = 640;
        win.height.value = 480;
    }
    
    模板
    struct TemplateParameterIndirection//这种方法适用于MinGW的GCC4.4.1,其他的都不知道
    {
    T型;
    };
    模板
    类吸气剂
    {
    公众:
    friend类TemplateParameterIndirection::Type;//Befriends模板参数
    模板
    Getter(Args Args):值(Args…{}//使用C++0x
    T get(){返回值;}
    受保护的:
    T值;
    };
    类窗口
    {
    公众:
    吸气剂宽度;
    吸气剂高度;
    调整空隙大小(uint32的宽度、uint32的高度)
    {
    //执行实际窗口大小调整逻辑
    width.value=width;//允许访问:Getter befriends窗口
    height.value=height;//此处相同
    }
    };
    void someExternalFunction()
    {
    窗口赢;
    win.resize(640480);//确定:公共方法
    //这是有效的:Getter::get()是公共的
    标准::cout
    
  • 您还可以使用getter或setter类型的方法来获取或设置可计算值,这与其他语言(如C)中使用属性的方式非常相似#

  • 我想不出合理的方法来抽象未知数量的值/属性的获取和设置

  • 我对C++ox标准不太熟悉,因此无法发表评论

  • 这就是我认为
    #define
    仍然有用的地方

    模板版本复杂且难以理解-定义版本显而易见

    #define Getter(t, n)\
         t n;\
         t get_##n() { return n; }
    
    class Window
    {
        Getter(int, height);
    }
    

    我确信我的语法有点错误,但你明白了


    如果boost中有一组众所周知的模板,那么我会使用它们。但我不会自己编写。

    FWIW以下是我对您的问题的看法:

  • 通常情况下,关键在于setter中有业务逻辑或其他强制约束。您还可以通过将实例变量与访问器方法解耦来获得计算变量或虚拟变量
  • 据我所知并非如此。我所从事的项目有一系列C宏来消除这种方法
  • 是的,我认为这很好。我只是担心这不值得麻烦,这只会让其他开发人员感到困惑(他们还需要一个概念放在他们的头脑中),并且不会节省太多的人工删除这些方法的费用

  • 在这种情况下,这可能有点过头了,但你应该看看律师/客户习惯用语,了解如何明智地使用友谊。在找到这个习惯用语之前,我完全避免了友谊


    虽然从实现的角度来看,解决方案很简洁,但从架构上看,它只是实现了一半。Getter/Setter模式的目的是让clas控制其数据并减少耦合(即其他类知道如何存储数据)。此解决方案实现了前者,但不完全是后者

    事实上,另一个类现在必须知道两件事——变量的名称和getter上的方法(即
    .get()
    ),而不是一件事——例如
    getWidth()
    。这会导致耦合增加

    说了这么多,这是众所周知的分裂建筑的头发。在一天结束时,这并不重要

    编辑好的,现在对于shits和giggles,这里有一个使用运算符的getter版本,因此您不必执行
    .value
    .get()

    模板
    struct TemplateParameterIndirection//这种方法适用于MinGW的GCC4.4.1,其他的都不知道
    {
    T型;
    };
    模板
    类吸气剂
    {
    公众:
    friend TemplateParameterIndirection::Type;//Befriends模板参数
    算子T()
    {
    返回值;
    }
    受保护的:
    T值;
    T&运算符=(T其他)
    {
    价值=其他;
    返回值;
    }
    };
    类窗口
    {
    公众:
    吸气剂宽度;
    吸气剂高度;
    空心调整大小(整型宽度、整型高度)
    {
    //执行实际窗口大小调整逻辑
    _宽度=宽度;//使用运算符
    _高度=高度;//使用运算符
    }
    };
    void someExternalFunction()
    {
    窗口赢;
    win.resize(640480);//确定:公共方法
    int w2=win.\u width;//使用运算符
    //赢。_height=480;//KABOOM
    }
    
    编辑修复了硬编码的赋值运算符。如果类型本身有赋值运算符,则该运算符应该可以正常工作。默认情况下,结构有赋值运算符,因此对于简单结构,它应该可以开箱即用


    对于更复杂的类,您将需要实现一个足够公平的赋值运算符。使用和优化,这在运行时应该是相当有效的。

    由于Igor Zevaka发布了一个版本,我将发布一个我很久以前编写的版本。这稍有不同——我当时观察到get/set的最实际使用pairs(实际上做了任何事情)是强制变量的值保持在预先确定的范围内,
    template <class T>
    struct TemplateParameterIndirection // This hack works for MinGW's GCC 4.4.1, dunno others
    {
        typedef T Type;
    };
    
    template <typename T,class Owner>
    class Getter
    {
    public:
        friend TemplateParameterIndirection<Owner>::Type; // Befriends template parameter
    
        operator T()
        {
            return value;
        }
    
    protected:
        T value;
    
        T& operator=( T other )
        {
           value = other;
           return value;  
        }
    
    
    };
    
    class Window
    {
    public:
        Getter<int,Window> _width;
        Getter<int,Window> _height;
    
        void resize(int width,int height)
        {
            // do actual window resizing logic
            _width = width; //using the operator
            _height = height; //using the operator
        }
    };
    
    void someExternalFunction()
    {
        Window win;
    
        win.resize(640,480); // Ok: public method
        int w2 = win._width; //using the operator
        //win._height = 480; //KABOOM
    }
    
    #include <exception>
    #include <iostream>
    #include <functional>
    
    template <class T, class less=std::less<T> >
    class bounded {
        const T lower_, upper_;
        T val_;
    
        bool check(T const &value) {
            return less()(value, lower_) || less()(upper_, value);
        }
    
        void assign(T const &value) {
            if (check(value))
                throw std::domain_error("Out of Range");
            val_ = value;
        }
    
    public:
        bounded(T const &lower, T const &upper) 
            : lower_(lower), upper_(upper) {}
    
        bounded(bounded const &init) 
            : lower_(init.lower), upper_(init.upper)
        { 
            assign(init); 
        }
    
        bounded &operator=(T const &v) { assign(v);  return *this; }
    
        operator T() const { return val_; }
    
        friend std::istream &operator>>(std::istream &is, bounded &b) {
            T temp;
            is >> temp;
    
            if (b.check(temp))
                is.setstate(std::ios::failbit);
            else
                b.val_ = temp;
            return is;
        }
    };
    
    
    #ifdef TEST
    
    #include <iostream>
    #include <sstream>
    
    int main() {
        bounded<int> x(0, 512);
    
        try {
            x = 21;
            std::cout << x << std::endl;
    
            x = 1024;
            std::cout << x << std::endl;
        }
    
        catch(std::domain_error &e) {
            std::cerr << "Exception: " << e.what() << std::endl;
        }
    
        std::stringstream input("1 2048");
        while (input>>x)
            std::cout << x << std::endl; 
    
        return 0;
    }
    
    #endif
    
    // Interface
    // Not how DEFINE does not repeat the type ;)
    #define DECLARE_VALUE(Object, Type, Name, Seq) **Black Magic Here**
    #define DEFINE_VALUE(Object, Name, Seq) ** Black Magic Here**
    
    // Obvious macros
    #define DECLARE_VALUER_GETTER(Type, Name, Seq)\
       public: boost::call_traits<Type>::const_reference Name() const
    
    #define DEFINE_VALUE_GETTER(Object, Name)\
       boost::call_traits<Name##_type>::const_reference Object::Name ()const\
       { return m_##Name; }
    
    #define DECLARE_VALUE_SETTER(Object, Type, Name)\
       public: Type& Name();\
       public: Object& Name(boost::call_traits<Type>::param_type i);
    
    #define DEFINE_VALUE_SETTER(Object, Name)\
       Name##_type& Object::Name() { return m_##Name; }\
       Object& Object::Name(boost::call_traits<Name##_type>::param_type i)\
       { m_##Name = i; return *this; }
    
    // window.h
    DECLARE_VALUE(Window, int, width, (GETTER)(SETTER));
    
    // window.cpp
    DEFINE_VALUE(Window, width, (GETTER)); // setter needs a bit of logic
    
    Window& Window::width(int i) // Always seems a waste not to return anything!
    { 
      if (i < 0) throw std::logic_error();
      m_width = i;
      return *this;
    } // Window::width
    
    #include <boost/preprocessor/seq/for_each.hpp>
    #include <boost/preprocessor/tuple/rem.hpp>
    
    #define DECLARE_VALUE_ITER(r, data, elem)\
      DECLARE_VALUE_##elem ( BOOST_PP_TUPLE_REM(3)(data) )
    
    #define DEFINE_VALUE_ITER(r, data, elem)\
      DEFINE_VALUE_##elem ( BOOST_PP_TUPLE_REM(2)(data) )
    
    #define DECLARE_VALUE(Object, Type, Name, Seq)\
       public: typedef Type Name##_type;\
       private: Type m_##Name;\
       BOOST_PP_SEQ_FOREACH(DECLARE_VALUE_ITER, (Object, Type, Name), Seq)
    
    #define DEFINE_VALUE(Object, Name, Seq)\
       BOOST_PP_SEQ_FOREACH(DEFINE_VALUE_ITER, (Object, Name), Seq)
    
    class Window
    {
      // Best get done with it
      DECLARE_VALUE(Window, int, width, (GETTER));
      DECLARE_VALUE(Window, int, height, (GETTER));
    
    // don't know which is the current access level, so better define it
    public:
    
    };