封装需要多长时间?

时间:2015-10-12 22:15:01

标签: c++ encapsulation

关于封装在C ++中的扩展程度是否存在普遍共识?我正在研究的具体示例是您是否应该限制对类中向量的访问。矢量应该与getVector()一起传递,还是应该限制为getVectorItem(int i)addVectorItem(int x)

public:
    vector<int>* getVector() const;

其中getVector返回一个指向矢量

的指针

public:
    int getVectorItem(int i) const;

getVectorItem(i)返回_vector[i]的任何内容。

你能抓住我的漂移吗?

3 个答案:

答案 0 :(得分:1)

每种不同的情况都需要不同的封装量,因此无法对一般性问题给出一般性答案。

我可能建议你只是让包含类的类可以直接迭代,例如:

class Foo
{
  private:
    vector<int> data;

  public:
    typedef typename decltype(data)::iterator iterator;
    typedef typename decltype(data)::const_iterator const_iterator;

    iterator begin() { return data.begin(); }
    iterator end() { return data.end(); }
    const_iterator begin() const ...
}

通过这种方式,您不会暴露Foo的内部结构,但您可以在标准STL算法等中使用它。当然,只有当你有一个包含数据的数据结构时,这才有效,除非你负责使用不同的命名方案来检索迭代器。

答案 1 :(得分:1)

两者并不相同。例如,前者允许调用者调整向量的大小,而后者则不然。所以这不是风格问题,而是实际的设计选择,只有你知道你希望外面的世界有多少能够改变。

答案 2 :(得分:-1)

根据用例,最好有一种方法来访问成员并修改它们而不暴露底层数据。在您的具体示例中,这意味着第二种选择是要走的路。

public:
  int getItem(int index) const;
  void setItem(int index, int item);

如果基础数据结构发生变化,您不必修改用户访问包装器的方式。如果要返回更复杂的数据,那么只需返回const引用即可明确用户应该如何访问数据。

public:
  ComplexClass const& getItem(std::string const& key) const;
  void setItem(std::string const& key, ComplexClass const& item);

与以前一样,通过拥有公共getter / setter,您可以防止用户对该类所具有的内部数据进行错误处理,并允许您实现任何类型的保护。

作为示例,如果用户返回一个数组,以阻止他们编辑错误的元素?

class Foo
{
public:
  std::vector<int> & getContainer() { return container; }

private:
  std::vector<int> container;
};

Foo foo;
std::vector<int> vector = foo.getContainer(); 
for(int i = 0; i < 10; ++i)
  vector.push_back(i);

vector[99999] = 0; // The program will crash here

使用自定义包装器,但我们可以防止崩溃

class Foo
{
public:
  Foo() : container(10) {}

  int getItem(int index) const
  { 
    if(index >= container.size() || index < 0)
      return -1; // error case
    else
      return container[index];
  }

  void setItem(int index, int item) 
  {
    if(index < container.size() && index > 0)
      container[index] = item;
  }

private:
  std::vector<int> container;
};

Foo foo;
for(int i = 0; i < 10; ++i)
  foo.setItem(i, i);

foo.setItem(99999, 0); // Nothing happens

您甚至可以添加迭代器运算符重载,以使访问外观与访问向量完全相同。但是,如果要在容器周围创建包装器,请确保实现可迭代的接口。