【问题标题】:Vectors Classes Private/Public向量类私有/公共
【发布时间】:2011-11-06 08:27:29
【问题描述】:

在 C++ 中,将类的数据保留为私有成员总是更好。
如果一个类有一个向量作为成员,最好把它作为私有成员还是公共成员?

如果我有一个向量作为私有成员,我将无法轻松访问该向量的成员函数。所以我必须为每个需要访问向量方法的函数设计一个方法?

给出的例子:

class MyClass{
private:
     std::vector<int> _myints;
public:
     get_SizeMyints(){return _myints.size();}
     add_IntToMyints(int x){_myints.push_back(x));
};

或者更好地保持向量公开并调用 MyClass._myints.push_back(x)?

----------编辑--------------

只是为了明确这个问题需要什么:

snake.h:

enum directions{UP, DOWN, RIGHT, LEFT, IN, OUT, FW, RW };


class Snake
{
private:
    enum directions head_dir;
    int cubes_taken;
    float score;
    struct_color snake_color;
    V4 head_pos;


public:

    std::vector<Polygon4> p_list; //the public vector which should be private...

    Snake();
    V4 get_head_pos();
    Polygon4 create_cube(V4 point);
    void initialize_snake();
    void move(directions);

    void set_head_dir(directions dir);
    directions get_head_dir();
    void sum_cubes_taken(int x);
    int get_cube_taken();

    void sum_score(float x);
    float get_score();

    void set_snake_color();



};

所以现在我知道如何更改代码了。

顺便说一句...一个问题,如果我需要像这样在其他类中复制向量:GlBox.p_list = Snake.p_list(如果是私有的,则有效)如果它们是私有的,那么什么是有效的方法?
运行 for 循环复制元素并将它们推回 GLBox.p_list 对我来说似乎有点低效(但可能只是一个印象):(

【问题讨论】:

  • MyClass 的目的是什么? (即它将如何使用,它代表什么,它应该看起来像vector 是否有意义等)
  • 应该是一条蛇,向量封装了构建蛇的立方体,所以它在运行时会改变长度..
  • 感谢大家的有用回答!

标签: c++ class vector private public


【解决方案1】:

如果有人来清空向量或重新排列它的所有元素并不重要,那么将其公开。如果它很重要,那么是的,您应该将其设为受保护/私有,并像您一样制作公共包装器。 [编辑] 既然你说“它是一条蛇”,那就意味着如果有人来移除或更换钻头,那就不好了。因此,您应该将其设为受保护或私有。 [/编辑]

你可以简化很多:

MyClass {
private:
     std::vector<int> _myints;
public:
     const std::vector<int>& get_ints() const {return _myints;}
     add_IntToMyints(int x){_myints.push_back(x));
};

get_ints() 函数将允许某人查看他们想要的所有向量,但不会让他们更改任何内容。但是,更好的做法是完全封装向量。这将允许您稍后用双端队列或列表或其他东西替换向量。您可以通过std::distance(myobj.ints_begin(), myobj.ints_end());获取尺寸

MyClass {
private:
     std::vector<int> _myints;
public:
     typedef std::vector<int>::const_iterator const_iterator;
     const_iterator ints_begin() const {return _myints.begin();}
     const_iterator ints_end() const {return _myints.end();}
     add_IntToMyints(int x){_myints.push_back(x));
};

【讨论】:

  • 提供get_ints() 是违反封装的。 MyClass 没有理由需要通过vector 实现——它可以很容易地成为dequelist 等。如果他以后更改底层实现,客户端使用get_ints() 中断的代码。
  • 确实如此。我认为一个简单的问题得到了一个简单的答案,但我编辑了我的答案以使其更完整,并显示出更好的封装性。
  • @Pella86:至于您的附带问题,如果 GIBox 可以在向量公开时执行 GlBox.p_list = Snake.p_list,那么对于我的代码,您会执行:GlBox.p_list.assign(Snake.ints_begin(), Snake.ints_end());。稍微冗长一些,但速度一样快。
【解决方案2】:

为了获得良好的封装性,您应该将向量保密。

【讨论】:

  • 另外,你的类不应该提供“操作向量的方法”,它应该提供“操作类处理的数据的方法”,它应该(透明地)对内部数据结构执行必要的操作.该类的用户不需要知道该类是否使用vector
  • 所以第二个问题是我如何访问成员?或者如何修改该向量的成员?
  • @Pella86:你不想修改向量的成员,你想修改向量的元素。您已经知道该怎么做。
【解决方案3】:

你的问题不是很具体,所以这里有一个相同的答案:

一般来说,你的类应该被设计成表达一个特定的概念和功能。他们不应该只是通过另一个成员类。如果你发现自己复制了一个成员对象的所有接口函数,那就有问题了。

也许有时你真的只需要其他东西的集合。在这种情况下,考虑一个普通的旧聚合,甚至是一个元组。但是,如果您正在设计一个适当的类,请使接口对手头的任务有意义,并隐藏实现。所以这里的主要问题是,为什么需要暴露向量本身?它在课堂上的作用是什么?就你的类的语义而言,它的空虚意味着什么?

找到合适的习语和想法来为您的班级设计一个最小的模块化界面,问题可能会自行消失。

(还有一个想法:例如,如果您有一些基于范围的需求,请考虑公开接受一对迭代器的模板成员函数。这样您就可以利用通用算法的强大功能,而无需依赖容器的选择。)

【讨论】:

    【解决方案4】:

    通常,良好的编码习惯是让您的数据成员保持私有或受保护,并提供访问它们所需的任何公共方法。不是(在这种情况下)vector 的所有方法,只是对您的应用程序有用的方法。

    【讨论】:

      【解决方案5】:

      这取决于你上课的目的。如果您只是尝试包装向量并希望将其用作向量,您可以为公开该向量提出一个论据。

      一般来说,我建议将其设为私有并提供适当的接口来操作容器。此外,如果其他容器更合适(只要您不将公共接口与容器类型绑定),这还可以让您在引擎盖下更改容器。

      另外,请避免以下划线开头的名称,因为有一些此类标识符是为实现而保留的,避免所有这些标识符比在所有情况下都尝试记住规则更安全。

      【讨论】:

      • 下划线后跟小写字母总是安全的,但最好指出以防 OP 不知道。
      • 实际上,如果一个包含 POSIX 合规性,列表会变得非常复杂,在阅读 this 之前,我也不知道一些警告。所以我同意“远离他们”。我当然更喜欢 python 的方法——远离__XX__,你很好——但是哦。
      【解决方案6】:

      要认识到,将std::vector 设为私有只是实现良好封装的一半。例如,如果您有:

      class MyClass {
          public:
              // Constructors, other member functions, etc.
      
              int getIntAt(int index) const;
      
          private:
              std::vector<int> myInts_;
      };
      

      ...那么可以说,这并不比公开myInts_ 更好。无论哪种方式,客户端都将使用MyClass 编写代码,这取决于底层表示需要使用std::vector 的事实。这意味着将来,如果您决定更高效的实现将使用 std::list 代替:

      class MyClass {
          public:
              // Constructors, other member functions, etc.
      
              int getIntAt(int index) const; // whoops!
      
          private:
              std::list<int> myInts_;
      };
      

      ...现在你有问题了。由于您无法通过索引访问std::list,因此您要么必须摆脱getIntAt,要么使用循环实现getIntAt。这两种选择都不好;在第一种情况下,您现在拥有的客户端的代码无法编译。在第二种情况下,您现在的客户端的代码只是默默地变得效率降低了。

      这是暴露任何特定于您选择的实现的公共成员函数的危险。在设计类接口时,牢记灵活性/未来维护非常重要。您可以通过多种方式使用您的特定示例来做到这一点;请参阅 Mooing Duck 对公开迭代器的此类接口的回答。

      或者,如果您想最大限度地提高代码的可读性,您可以围绕MyClass 逻辑表示的内容设计界面;在你的情况下,一条蛇:

      class MyClass {
          public:
              // Constructors, etc.
      
              void addToHead(int value);
              void addToTail(int value);
              void removeFromHead();
              void removeFromTail();
      
          private:
              // implementation details which the client shouldn't care about
      };
      

      这在您的程序中提供了一个蛇对象的抽象,并且简化的接口使您可以灵活地选择最适合它的任何实现。如果出现这种情况,您始终可以在不破坏客户端代码的情况下更改该实现。

      【讨论】:

        【解决方案7】:

        从理论上讲,在面向对象编程中,任何属性都应该是私有的,并且可以通过 Get() 和 Set() 等公共方法访问它们。 我认为您的问题并不完整,但是我从您要实现的目标中了解到,您需要从 std::vector 继承并扩展其功能,以满足您的快速访问需求并且不搞乱封装。 (考虑首先从任何 C++ 书籍或其他 OO 语言中阅读“继承”

        话虽如此,您的代码可能如下所示:

        class MyClass : public std::vector<int>
        {
             //whatever else you need goes here
        }
        
        int main(void)
        {
             MyClass var;
             var.push_back(3);
             int size = var.size(); // size will be 1
        }
        

        希望这回答了你的问题

        【讨论】:

        • 从具有非虚拟析构函数的类继承是不好的做法。没有一个 STL 容器被设计为基类,不应该这样使用。
        • @Toolbox。我的错,我习惯了 MFC 容器并且那些有虚拟析构函数 AFAIK,所以我认为标准容器也有它。
        猜你喜欢
        • 2011-12-03
        • 2011-05-25
        • 2014-08-21
        • 2015-05-04
        • 2018-05-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-11
        相关资源
        最近更新 更多