29

这确实是一个良好形式/最佳实践的问题。我使用 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;}
}

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

4

7 回答 7

27

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

  1. 如果该类有一些不变量,则通过接口隐藏该类的表示。

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

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

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

    请参阅:类应该强制执行不变量

  2. 非友元非成员函数提高了封装性。

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

    如果您确实将其设为成员函数,并且有一天向 引入了一些私有状态Person,那么您将有不必要的依赖。突然age拥有比它需要的更多的数据访问权限。

    请参阅:非成员函数如何改进封装

所以这就是我将如何实现它:

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

int age(const Person& person) {
  return calculated age from person.dob;
}
于 2013-03-22T14:43:45.663 回答
6

在 C++ 中,Structs 是类,唯一的区别(至少我能想到)是 Structs 中的成员默认情况下是公共的,但在类中它们是私有的。这意味着完全可以按照您的方式使用 Structs -这篇文章很好地解释了这一点。

于 2013-03-22T14:24:37.573 回答
1

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

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

于 2013-03-22T14:29:50.070 回答
1

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

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

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

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

于 2013-03-22T14:33:09.693 回答
0

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

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

于 2013-03-22T14:26:51.450 回答
0

我不想在这里引发一场圣战;我通常以这种方式区分它:

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

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

结构中!您可以拥有封装可见性运算符(私有公共)!就像你上课一样!

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

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

运行下面的代码!你可以检查它是否编译得很好!并且运行良好!整个结构就像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;
}

在此处输入图像描述

在记忆中他们是一样的!

我没有检查自己!但有人说,如果你做同样的事情!编译后的汇编代码在结构之间是一样的!(被检查!)

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

class Client {

}

Client client(...);

=>

typedef struct Client {
 ....
} Client;

Client client(...);

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

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

于 2021-06-05T21:40:41.453 回答