通过引用返回或创建一个典型的setter / getter?

时间:2013-04-26 20:11:19

标签: c++

我想知道C ++中的良好实践,我遇到了为类成员制作getter / setter的问题。

那么,为什么不简单地通过引用返回成员,这样我可以修改或访问它的值来读取它?具体来说,这是我的代码:

class Chest : public GameObject
{
public:
    Chest();
    ~Chest();

    static int& num_chests();

private:
    static int num_chests_;
};

这是一种不好的做法吗?我应该使用这些吗?

class Chest : public GameObject
{
public:
    Chest();
    ~Chest();

    static int num_chests();
    static void set_num_chests(int num_chests);

private:
    static int num_chests_;
};

4 个答案:

答案 0 :(得分:11)

除非你强烈反对,否则请使用getter和setter成员函数。

int& num_chests()或公共字段错误的原因是您将使用num_chests值的客户端代码与实际上是字段(内部实现细节)的事实相结合。

假设您以后决定在课堂上拥有std::vector<Chest> chests个私人字段。然后你不希望有一个int num_chests字段 - 这是非常多余的。您可能希望拥有int num_chests() { return chests.size(); }

如果您使用的是公共字段,那么现在您的所有客户端代码都需要使用此函数而不是之前的字段访问 - 需要更新num_chests值的每次使用,因为接口已更改

如果您使用的是返回引用的函数,那么您现在遇到问题,因为chests.size()是按值返回的 - 您无法通过引用返回该值。

始终封装您的数据。它只需要极少量的样板代码。

回应评论说你应该只使用公共字段:

请记住,使用公共字段的唯一好处(除了一些微优化的远程可能性之外)是您不必编写样板代码。 “当我使用公共场地时,我的老师常常讨厌(而且他太烦人了)”使用公共领域是一个非常糟糕的论据。

答案 1 :(得分:2)

几乎在所有情况下,当你认为你必须创建setter AND getter时(也就是两者同时),你的设计是错误的。

改为考虑num_chests的目的是什么?你不能在不知道它是什么的情况下去任何地方。

通过你的代码,我猜它包含了关卡的数量。在这种情况下,您不希望为每个人提供此值的setter。您希望该值等于游戏中的箱子数量,并且通过在此处提供setter,每个人都可以使该不变量无效。

相反,你可以只提供getter,你可以控制它在你班级中的价值。

class Chest : public GameObject
{
public:
    Chest() { ++num_chests_; }
    ~Chest() { --num_chests_; }

    static int num_chests() { return num_chests_; }

private:
    static int num_chests_;
};

int Chest::num_chests_ = 0;

在我看来,有关为什么吸气剂和制定者错误决定的更多解释。 如果你提供了setter和getter,你只能控制变量。考虑std::complex。为什么

std::complex<double> my_complex(1.0, 5.0);
my_complex.real(my_complex.real()+1);

更好

std::complex<double> my_complex(1.0, 5.0);
my_complex.real()++;

答案:设计std::complex时,C ++中没有引用。此外,Java没有C ++风格的引用,因此它们必须在任何地方编写样板代码。现在,GCC在这里返回非const引用作为扩展,而C ++ 11允许

reinterpret_cast<double (&)[2]>(non_const_non_volatile_complex_variable)[0]

reinterpret_cast<double (&)[2]>(non_const_non_volatile_complex_variable)[1]

是访问std::complex<value>的实部和虚部的有效方法。

答案 2 :(得分:2)

您的界面的目的是是最简单的程序,但最简单的使用扩展

如果您未能提供setter和getter方法,那么您可能会为以后的头痛做好准备。例如:

  • 如果您需要在有人更改num_chests
  • 的值时发出通知,会发生什么情况
  • 如果您需要验证,num_chests不能为负,会发生什么?
  • 如果您需要在多线程环境中运行程序并且需要锁定读取直到写入准备就会发生什么?

正如您所看到的,对用户透明的界面也更容易防止用户错误,并且还可以在将来扩展;这个优势很少(如果有的话)额外成本。

另一方面,有时您确实想要返回指向内部成员的引用或指针。例如,标准库中的容器类通常提供data()方法,该方法检索指向底层容器的指针(const和非const变体)。

所以,这不是一个很难的规则,但我会说返回私有成员的非const引用会破坏OO编程的目的。

答案 3 :(得分:0)

我的目标是低耦合和高内聚;我避免使用吸气剂和吸尘剂。

如果你有getter和setter,另一个对象将不得不知道如何调用它们。这是耦合。

尝试将对象分离,但要使它们具有凝聚力。通过凝聚力,我的意思是它们与系统的其他部分配合良好。

你的系统是什么?为什么你有吸气剂和二传手?因为您想要控制和显示这些对象。他们是模型,你有控制器和视图。

很容易陷入控制/视图与模型之间耦合的陷阱。

为避免耦合,让模型创建控件并更新视图。然后它不必有任何吸气剂或制定者。

e.g。

struct Instrumenter {
    virtual void addRange(int& value, int min, int max) = 0;
};

struct Renderer {
    virtual void render(std::string& description, int value) = 0;
};

struct GameObject {
    virtual void instrument(Instrumenter& instrumenter) = 0;
    virtual void display(Renderer& renderer) = 0;
};

struct Chest : GameObject {
    virtual void instrument(Instrumenter& instrumenter) {
        intrumenter.addRange(count, 0, 10);
    }
    virtual void display(Renderer& renderer) {
        renderer.render("Chest count", count);
    }
private:
    int count;
};

然后你可以像这样使用它:

int main() {
    vector<shared_ptr<GameObject>> gameObjects;
    MyControlInstrumenter instrumenter;
    // ...
    for(auto& gameObject: gameObjects) {
        gameObject->instrument(instrumenter);
    }
    // etc.
}