Accessor函数通过数据成员分配

时间:2017-12-19 14:24:45

标签: c++ c++11

我想增强C ++类的成员,以便从/向它们的赋值导致调用自定义getter / setter。

class Class
{ 
public:
    int Member;
    void SetMember(int Value); // TBD
    int GetMember(); // TBD
}

Class Instance;
Instance.Member= 3; // Causes a call to SetMember(3);
int Data= Instance.Member; // Causes a call to GetMember();

我找到了一种方法来强制对成员赋值进行函数调用,方法是将成员类型转换为包含私有值的类,并重载强制转换的运算符和赋值运算符以进行写入。

class WrapInt
{
public: 
    operator int() const { return Value; }
    void operator=(const int Assign) { Value= Assign; }
private:
    int Value;
}

这是有效的,但是以通用方式,我不能为每个成员自定义getter / setter,而只能为每种数据类型定制。

您是否看到了一种改进方法,以便为不同类型的成员编写不同的访问者?

更新

我现在找到了满足我需求的解决方案。需要特殊setter的成员使用知道托管类的类来定义:

template<class Parent> class WrapInt
{
public:
    int operator=(const int Value) {  (This->*Setter)(Value); return Value; }
    operator int() { return Value; }

private:
    int Value;
    Parent* This;
    void (Parent::*Setter)(int Value);
    friend Parent;
};

分配这样的成员会调用赋值运算符,该运算符通过指向主类方法的指针调用普通的setter函数。 Get操作是通过强制转换操作符实现的,该操作符只返回成员值(该方案可能会增强以支持自定义getter,但我并不需要)。

这是非常昂贵的,因为每个成员都需要额外的This指向类实例和指向setter的指针;这些需要在类构造函数中初始化(如果没有,则保证崩溃)。

所以这需要在类编写器方面做一些努力(在构造函数中初始化),但是然后分配给成员会自动调用setter,如我所愿。

class Class
{ 
public:
    Class();
    WrapInt<Class> Member;

private:
    void SetMember(int Value); // TBD
}

Class::Class() { Member.This= this; Member.Setter= &Class::SetMember; }

在用户一侧,

Class Instance;
Instance.Member= 3; // Calls the setter SetMember
cout << Instance.Member;

是的。

3 个答案:

答案 0 :(得分:0)

代码的修改版本:

class WrapInt
{
public:
    WrapInt(std::function<int()> getter, std::function<void(int)> setter) :
        getter(getter),
        setter(setter)
    {}

    WrapInt(const WrapInt&) = delete;
    WrapInt& operator =(const WrapInt&) = delete;

    operator int() const { return getter(); }
    void operator=(int value) { setter(value); }
private:
    std::function<int()> getter;
    std::function<void(int)> setter;
};

class Class
{ 
public:
    Class() : Member([this](){ return this->GetMember();},
                     [this](int value) {SetMember(value); })
    {}

    WrapInt Member;
    void SetMember(int Value); // TBD
    int GetMember(); // TBD
};

答案 1 :(得分:0)

您可以使您的班级WrapInt可修改。

选项1:在运行时,使用函数对象

class WrapInt
{
public: 
    operator int() const { return Value; }
    void operator=(const int Assign) 
    { 
        assign_callback(Assign); 
        Value = Assign; 
    }

private:
    int Value;
    std::function<void (int)> assign_callback;
}

在此变体中,您必须在包含类的构造函数中分配正确的回调:

class Container 
{
    WrapInt a, b, c;
    Container () 
    {
        a.assign_callback = ...;
        b.assign_callback = ...;
        c.assign_callback = ...;
    }
}

选项2:在编译时,使用继承

class WrapInt
{
public: 
    operator int() const { return Value; }
    void operator=(const int Assign) 
    { 
        assign_callback(Assign); 
        Value = Assign; 
    }

private:
    int Value;
    virtual void assign_callback(int) = 0;
}

在此变体中,您将在包含类的类主体中多次从WrapInt继承

class Container 
{
    class WrapIntA : public WrapInt {
        void assign_callback() { ... };
    } a;
    class WrapIntB : public WrapInt {
        void assign_callback() { ... };
    } b;
    class WrapIntC : public WrapInt {
        void assign_callback() { ... };
    } c;
}

答案 2 :(得分:0)

不要与语言作斗争:C ++不支持对函数的get / set绑定。你只需要容忍

Instance.Member() = 3;

int Data = Instance.Member();

您可以通过提供返回const引用的Member()函数const和返回非const版本的非const版本来提供参考。

对C ++的一个批评是你需要编写的样板量,特别是如果你需要为你的类中的每个成员变量都需要它。但实际上,在这一点上,你几乎完全避免了封装:除非你的函数进行一致性检查,否则你也可以让成员public