【问题标题】:C++ Structs with Member Functions vs. Classes with Public Variables具有成员函数的 C++ 结构与具有公共变量的类
【发布时间】:2013-03-22 14:21:18
【问题描述】:

这确实是一个良好形式/最佳实践的问题。我使用 C++ 中的结构来形成旨在基本上保存数据的对象,而不是创建一个具有大量访问器方法的类,这些方法除了获取/设置值之外什么都不做。例如:

struct Person {
    std::string name;
    DateObject dob;
    (...)
};

如果您想象那里还有 20 个变量,那么将其编写为具有私有成员和 40 多个访问器的类会很麻烦,而且对我来说似乎很浪费。

但有时,我可能还需要为数据添加某种最小功能。在示例中,假设我有时还需要年龄,基于 dob:

struct Person {
    std::string name;
    DateObject dob;
    (...)
    int age() {return calculated age from dob;}
}

当然,对于任何复杂的功能,我都会创建一个类,但是对于像这样的简单功能,这是“糟糕的设计”吗?如果我确实使用了一个类,将数据变量保留为公共类成员是一种不好的形式,还是我只需要接受它并使用一堆访问器方法创建类?我了解类和结构之间的区别,我只是在询问最佳实践。

【问题讨论】:

  • 编写访问方法(getter/setter)是个好主意。如果你只是谷歌一下,你可能会发现关于这个话题的大量讨论。提示:大多数 IDE 都提供了自动生成 getter 和 setter 的功能。
  • classstruct 类型的不同之处在于class 的默认访问控制是private,而struct 的默认访问控制是公共的。在创建类型之外使用关键字可能略有不同......但您似乎投入了大量精力来区分类型中的单词structclass。它可用于特定项目中的文档,但这是特定于项目的。
  • 还有免费功能int age(const Person& person)的选项。并非所有东西都必须是带有方法的对象。

标签: c++ class struct


【解决方案1】:

我认为这里有两个重要的设计原则需要考虑:

  1. 如果某个类存在不变量,则通过接口隐藏该类的表示。

    当该类存在无效状态时,该类具有不变量。该类应始终保持其不变性。

    考虑一个表示二维几何点的Point 类型。这应该只是带有公共 xy 数据成员的 struct。没有无效点之类的东西。 xy 值的每个组合都非常好。

    对于Person,它是否具有不变量完全取决于手头的问题。您认为空名称之类的东西是有效名称吗? Person 可以有任何出生日期吗?对于您的情况,我认为答案是肯定的,您的班级应该公开成员。

    见:Classes Should Enforce Invariants

  2. 非友元非成员函数改进了封装性。

    没有理由将您的age 函数实现为成员函数。 age 的结果可以使用Person 的公共接口计算,所以它没有理由成为成员函数。将它放在与Person 相同的命名空间中,以便通过参数相关查找找到它。 ADL 找到的函数是该类接口的一部分;他们只是无权访问私人数据。

    如果你确实让它成为一个成员函数,并且有一天向Person 引入了一些私有状态,你就会有一个不必要的依赖。突然间,age 对数据的访问权限超出了它的需要。

    见:How Non-Member Functions Improve Encapsulation

所以我将如何实现它:

struct Person {
  std::string name;
  DateObject dob;
};

int age(const Person& person) {
  return calculated age from person.dob;
}

【讨论】:

  • 很好的答案,正是我正在寻找的信息类型,谢谢!
  • 我同意这个答案。我还要补充一点,将age 定义为成员函数是较差的控制耦合——调用者必须将当前日期传递给该方法才能很好地了解它在做什么,或者当前日期必须是从其他地方检索,这对调用者来说并不明显。 age 向客户端接口公开了比所需类型更多的实现。如果保证这个结构永远不会变得更复杂,它可能会合理地保留为成员函数,但即便如此,我至少会声明它inline 并添加一个currentDate 参数。
  • @Joseph Mansfield “将它放在与Person 相同的命名空间中”,我们难道不希望看到namespace PeopleStuff {...} 封装您的代码块吗?或者age() 在同一个文件中就足够了吗?
  • "将它放在与 Person 相同的命名空间中,以便通过依赖于参数的查找找到它。ADL 找到的函数是该类接口的一部分;它们只是无权访问私有数据。” == 脑洞大开。 nobism 问了一个很好的问题,你能详细说明一下吗?
【解决方案2】:

在 C++ 中,Structs 是类,唯一的 区别(至少我能想到)是 Structs 中的成员默认情况下是公共的,但在类中它们是私有的。这意味着您可以完全接受 Structs - this article 解释得很好。

【讨论】:

    【解决方案3】:

    在 C++ 中,结构和类之间的唯一区别是结构在默认情况下是公开可见的。一个很好的指导方针是将结构用作纯旧数据 (POD),它只保存数据并在需要更多功能(成员函数)时使用类。

    您可能仍然想知道是在类中只使用公共变量还是使用成员函数;考虑以下场景。

    假设你有一个类A,它有一个函数GetSomeVariable,它只是一个私有变量的getter:

    class A
    {
        double _someVariable;
    
    public:
        double GetSomeVariable() { return _someVariable; }
    };
    

    如果二十年后,该变量的含义发生了变化,并且您必须将其乘以 0.5,该怎么办?使用 getter 时,很简单;只需返回变量乘以 0.5:

        double GetSomeVariable() { return 0.5*_someVariable; }
    

    通过这样做,您可以轻松维护并轻松修改。

    【讨论】:

    【解决方案4】:

    如果你想要一些数据持有者,那么更喜欢没有任何 get/set 方法的结构。

    如果还有更多内容,例如本例中的“Person”。

    1. 它模拟现实世界的实体,
    2. 具有明确的状态和行为,
    3. 与外部世界交互,
    4. 展示与其他实体的简单/复杂关系,
    5. 它可能会随着时间的推移而演变,

    那么它是一个类的完美候选者。

    【讨论】:

      【解决方案5】:

      “仅对携带数据的被动对象使用结构;其他一切都是类。”

      google guidlines,我就是这样做的,发现这是一个很好的规则。除此之外,我认为你可以定义自己的语用学,或者如果它真的有意义的话,可以偏离这个规则。

      【讨论】:

        【解决方案6】:

        我不想在这里引发一场圣战;我通常是这样区分的:

        • 对于 POD 对象(即,仅数据,没有暴露行为)将内部声明为公共并直接访问它们。 struct 关键字的使用在这里很方便,也可以作为对象使用的提示。
        • 对于非 POD 对象,声明内部私有并定义公共 getter/setter。在这些情况下,class 关键字的使用更加自然。

        【讨论】:

          【解决方案7】:

          只是为了消除一些人的困惑!而且采摘方便!这里有几点!

          结构中!你可以有 encapsulationvisibility 操作符(使 privatepublic)!就像上课一样!

          所以有人说或者你可能在网上找到的说法是:其中一个区别是结构没有可见性运算符和隐藏数据的能力,是错误的!

          您可以像在类中一样拥有方法!

          运行下面的代码!你可以检查它是否编译得很好!并且运行良好!整个 struct 就像 class 一样工作!

          主要区别只是可见模式的默认设置!

          结构已公开!默认为私有类!

          #include<iostream>
          #include<string>
          
          using namespace std;
          
          int main(int argv, char * argc[]) {
              struct {
                  private:
                      bool _iamSuperPrivate = true;
                      void _sayHallo() {
                          cout << "Hallo mein Bruder!" << endl;
                      }
                  public:
                      string helloAddress = "";
                      void sayHellow() {
                          cout << "Hellow!" << endl;
                          
                          if (this->helloAddress != "") {
                              cout << this->helloAddress << endl;
                          }
          
                          this->_sayHallo();
                      }
                      bool isSuperPrivateWorking() {
                          return this->_iamSuperPrivate;
                      }
              } testStruct;
          
              testStruct.helloAddress = "my Friend!";
              testStruct.sayHellow();
          
              if (testStruct.isSuperPrivateWorking()) {
                  cout << "Super private is working all well!" << endl;
              } else {
                  cout << "Super private not working LOL !!!" << endl;
              }
          
              return 0;
          }
          

          在记忆中它们是一样的!

          我没有检查自己!但有人说,如果你做同样的事情! structclass 之间的编译汇编代码将相同! (待查!)

          参加任何课程并将名称更改为 typedef struct !您会看到代码仍然可以正常工作!

          class Client {
          
          }
          
          Client client(...);
          

          =>

          typedef struct Client {
           ....
          } Client;
          
          Client client(...);
          

          如果你这样做,一切都会一样!至少我知道在 gcc 中是这样的!

          你可以测试!在您的平台上!

          【讨论】:

            猜你喜欢
            • 2015-05-25
            • 1970-01-01
            • 2011-04-19
            • 2012-04-12
            • 1970-01-01
            • 1970-01-01
            • 2021-09-07
            • 2017-02-19
            相关资源
            最近更新 更多