【问题标题】:In C++ is it possible to call an accessor through property-like syntax?在 C++ 中是否可以通过类似属性的语法调用访问器?
【发布时间】:2016-04-22 16:22:57
【问题描述】:

我正在处理一个大型代码库,并且有许多公开定义的变量。不幸的是,访问这些变量的功能已经改变,这个新功能最好由公共​​访问器和私有实例变量封装。

所以,我正在尝试进行此更改。为此,我计划将每个公共属性设为私有,然后创建访问器。但是,我不想更改任何访问旧公共属性的代码。例如:

将公共属性更改为私有后,我有以下类:

class Test {

    private:
        int item = 5;

    public:
        int GetItem() {
             return item;
        };

        void SetItem(int new_item) {
             item = new_item;
        };
};

过去,“item”是类的公共属性,通过以下方式访问:

Test* t = new Test();
int item = t->item;

现在,我需要为检索“项目”的方式添加新功能。例如:

int GetItem() {
     //  Some complicated code which changes "item"
     return item;
};

我怎样才能保持相同的语法:

int item = t->item;

但是让这个实际执行:

int item = t->GetItem();

非常感谢任何帮助!

【问题讨论】:

  • 不,C++ 是一种已编译的非反射性语言,也就是说,您不能只是“在访问元素时查找名称”,因为在二进制文件中,不再有名称。另外,请不要仅仅为了拥有 getter 和 setter 就让你的 C++ 变成 Java——如果它们实际上没有增加安全性,我真的看不出使用它们的意义。
  • @MarcusMüller 如果它们存在,属性将不需要任何反射或运行时支持。它们可能纯粹是编译器扩展的方法调用的语法糖。当然,它们不存在,但不是因为你暗示的原因。
  • 通过 getter 和 setter 进行封装是有原因的,如果没有这个原因,你不需要仅仅为了封装而需要 getter 和 setter
  • “它是通过以下方式访问的:Test t = new Test(); 我对此表示怀疑。那甚至不会编译。
  • @delnan 如果它们存在,那么这些语法糖属性会导致悖论——该属性的地址是什么?它的类型是什么?不,对于严格静态类型的语言,拥有这样的属性会带来很大的不便。

标签: c++ refactoring encapsulation accessor


【解决方案1】:

您可以通过将 item 定义为成员变量来使 int item = t.item; 工作,其类型是定义了自定义转换 operator int() 的辅助类。还有,operator=(int new_value)拦截设置操作。

你做不到的是

int& item = t.item;

int* pitem = &t.item;

因为这两者都可以直接访问内存,而无需通过任何 getter 或 setter。在创建引用或指针时,您甚至无法确定将有多少次访问或它们是读取还是写入。

【讨论】:

  • 感谢您的回答。然而,在我的情况下,我需要使用指针来执行此操作,因此我最终手动更改了大部分代码库。
【解决方案2】:

C++ 是一种已编译的非反射性语言,即您不能只是“在访问元素时查找名称”,因为在二进制文件中,不再有名称。

所以,不,你想要的是不可能的。 (至少并非没有限制——参见 Ben Voigt 的 excellent answer;拥有一个实际上是 getter 调用的“透明”属性肯定不值得你用它构建的陷阱——)

另外,请不要仅仅为了拥有 getter 和 setter 而让你的 C++ 变成 Java——如果它们实际上并没有增加安全性,我真的不明白使用它们的意义

【讨论】:

    【解决方案3】:

    如果您的问题是基于您不想调用 2 个不同的函数进行设置和获取,您可以创建一个返回成员引用的函数:

    int& Item()
    {
         // Some complicated code which changes *items
         return item;
    }
    

    如您所见,返回类型是 int& 而不是 int。所以你可以这样使用这个功能

    t.Item() = someValue;
    

    【讨论】:

    • 感谢您提供此信息。但就我而言,我希望在添加这个新功能时保持现有代码库不变。
    【解决方案4】:

    要扩展 Ben Voight 的答案,您可以定义一个代理模板,无需样板即可:

    template <typename Return, typename Containing, Return (Containing::* func)()>
    struct proxy
    {
       Containing& c;
       proxy(Containing& c) : c(c) {}
       operator Return() { return (c.*func)(); }
       Return& operator=(const Return& r) { return (c.*set)() = r; }
     };
    

    然后定义一个“属性”

    class c {
       int y_;
       int get_y() { std::cout << "Getting y" << std::endl; return y_; }
    public:
       proxy<int, x, &x::get_y> y;
       c() : y(*this) {}
    };
    

    在客户端代码中

    int main() {
       c val;
       val.y = 5;
       std::cout << val.y << std::endl;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多