12

我想知道 C++ getter 和 setter 的好语法。

private:
YourClass *pMember;

我猜二传手很容易:

void Member(YourClass *value){
  this->pMember = value; // forget about deleting etc
}

和吸气剂?我应该使用引用还是常量指针?

例子:

YourClass &Member(){
   return *this->pMember;
}

或者

YourClass *Member() const{
  return this->member;
}

它们之间有什么区别?

谢谢,

编辑:

抱歉,我将编辑我的问题...我知道引用和指针,我问的是引用和 const 指针,作为吸气剂,它们在我的代码中会有什么区别,就像在未来一样,我应该期待什么如果我走另一条路就会输……

所以我想我会使用 const 指针而不是引用

const 指针不能被删除或设置,对吧?

4

8 回答 8

17

作为一般规律:

  • 如果 NULL 是有效参数或返回值,请使用指针。
  • 如果 NULL不是有效的参数或返回值,请使用引用。

因此,如果可能应该使用 NULL 调用 setter,请使用指针作为参数。否则使用参考。

如果调用包含 NULL 指针的对象的 getter 是有效的,它应该返回一个指针。如果这种情况是非法的不变量,则返回值应该是一个引用。如果成员变量为 NULL,则 getter 应抛出异常。

于 2009-10-20T18:25:07.380 回答
7

最好的办法是为隐藏实现细节的客户端提供一个真正的 OO 接口。Getter 和 Setter 不是面向对象的。

于 2009-10-20T18:32:41.123 回答
7

您的代码看起来很棒,就好像您习惯了另一种语言一样——在 C++ 中使用this->x(例如)相对不寻常。当代码写得很好时,使用访问器或修改器也是如此。

虽然我在这方面相当不寻常,但我会继续记录(再次)说,强制客户端代码直接使用访问器或修改器是一个坏主意。如果您确实遇到客户端代码操作对象中的值有意义的情况,那么客户端代码应该使用正常赋值来读取和/或写入该值。

当/如果您需要控制分配的值时,运算符重载可以让您进行控制,而不会在客户端代码上强制使用丑陋的 get/set 语法。具体来说,您想要的是一个代理类(或类模板)。仅举一个例子,人们想要获取/设置函数的最常见情况之一是应该限制在某个特定范围内的数字。检查新setXXX值是否在范围内,然后getXXX返回该值。

如果你想要这样,一个(相当)简单的模板可以更干净地完成这项工作:

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;
    }
};

这也使得代码更接近于自我记录——例如,当你声明一个像这样的对象时bounded<int>(1, 1024);,很明显意图是一个 1 到 1024 范围内的整数。有人可能会发现有问题的唯一部分是范围内是否包含 1 和/或 1024。这与在类中定义一个 int 有很大不同,并且期望每个看过该类的人都意识到他们应该使用 setXXX 来强制对可以是分配的。

当您将其中一个嵌入到一个类中时,您将其设为公共变量,并且仍然强制执行该范围。在客户端代码中,语法没有真正的争论——你只是分配给一个公共变量,就像你做任何其他的一样——尝试分配一个超出范围的值会引发异常的次要细节。从理论上讲,该类可能应该采用策略模板参数来准确指定它在这种情况下的作用,但我从来没有真正的理由为此烦恼。

于 2009-10-20T19:40:01.840 回答
4

正如其他人所说,如果有可能,请使用指针。

在大多数情况下,我更喜欢尽可能使用引用。就个人而言,在我的代码中,我喜欢使用指针和引用之间的区别来表示所有权。我认为带有引用的调用是将对象“借给”另一个函数或类。传递或返回引用的原始类仍然拥有它,并负责它的创建、维护和清理。另一方面,当我的代码传递一个非常量指针时,这通常意味着正在进行某种所有权转移或共享,并承担所有责任。

(是的,我通常使用智能指针。在我看来,这些类似于引用。我说的是比这里更低级别的代码。)

于 2009-10-20T18:37:58.250 回答
2

它们之间有什么区别?

引用是事物的别名(它事物*)。指针是事物的地址。如果有可能指向的内容不存在,那么您可能不想返回引用。引用告诉调用者“我会给你一个别名,当我把它还给你时它就会存在”。事实上,真的没有办法检查参考资料以查看底层内容是否有效。

从语义上讲,使用指针意味着调用者可能希望在使用之前检查 Member 是否存在。通常这是通过 NULL 检查来完成的。

最终没有“正确”的答案。这取决于班级的合同以及调用者是否会/应该/想要检查“成员”是否仍然存在。

简短的回答是指向可以指向其他地方的事物的指针以及对“未就位”别名的引用。

于 2009-10-20T18:25:44.947 回答
2

除了其他答案之外,如果您为 getter 选择引用,请不要像您的示例中那样编写它:

YourClass &Member(){
   return *this->pMember;
}

您的 getter 实际上允许设置,如 ininstance->Member() = YourClass();并因此绕过您的 setter。如果 YourClass 是不可复制的,这可能是不允许的,但这仍然是另一回事。另一个缺点是 getter 不是 const。

相反,像这样写你的吸气剂:

const YourClass &Member() const {
   return *this->pMember;
}
于 2009-10-20T20:08:38.950 回答
1

+1 质疑 setter 和 getter 的使用。如果您必须使用它们并且有可能出现空值,请考虑使用 boost::shared_ptr。这样就可以为您处理所有权。

于 2009-10-20T18:37:31.450 回答
0

乔纳森,你用的是什么编译器?作为编译器的 TR1 实现的一部分,它很有可能shared_ptr已经随附。

于 2009-10-20T19:26:21.920 回答