什么时候应该在C ++中使用类与结构?


949

在什么情况下,在C ++中使用structvs 会更好class


47
这不仅适用于C ++,而且适用于同时提供结构和类的任何语言。
詹森·邦廷

3
我仍然不同意-我从语义上解决了这个问题。也许有些技术上的差异,但从语义上讲,没有。结构对于创建值类型非常有用,而类则没有。
詹森·邦廷

4
我相信在C ++中没有使用结构的严肃理由。对我而言,结构是C ++的另一个冗余“功能”,仅用于与C兼容,例如typedef。如果最初不将C ++视为C的扩展,而是从头开始设计的,例如Java,则这些将不存在。通常,我发现关于C ++的许多最奇怪的事情与C兼容性有关。
Kostas,2009年

5
结构-对于POD(普通旧数据),所有成员可访问性都是公开的。类-需要更好的封装并且需要成员函数使用类的状态时。
Navaneeth KN

4
仅按照惯例,这是正确的。除了默认封装外,没有任何区别。
戴夫·希利尔

Answers:


804

C ++中a class和a 之间的区别在于struct,结构具有默认public成员和基数,而类具有默认private成员和基数。两个类和结构可具有的混合物publicprotectedprivate构件,可以使用继承并且可以具有成员函数。

我建议将结构用作没有任何类功能的纯旧数据结构,并建议将类用作具有private数据和成员函数的聚合数据结构。


99
没有修饰符或方法的结构称为POD结构,它作为与C库的向后兼容接口而存在,因为它(假定)被保证像C结构一样被布局。除了这一例外之外,唯一的区别是如上所述。
workmad3

26
@ workmad3:这个名称具有误导性,但是9/4(C ++ 03)说:“ POD结构是一个聚合类,它没有非POD结构,non-POD-union类型的非静态数据成员(或此类数组)或引用,并且没有用户定义的副本分配运算符,也没有用户定义的析构函数。” 使用“ struct”类密钥没有限制,也没有使用“ public”的限制(有关聚合要求,请参见8.5.1 / 1)。这在“结构”和“类”之间没有区别。

5
给定标准的定义,您对“集合”的使用可能会被误解。:)

3
根据Stroustrup的“原理和实践”书:“结构应主要用于成员可以取任何值的地方”(即,在没有定义有意义的类不变式的地方)
antibus 2014年

6
当然,在与C接口时可以使用类。类和结构之间没有区别。结构是类;只有默认访问权限从私有切换为公开。
塞巴斯蒂安·马赫

229

正如其他所有人所指出的,实际上只有两种实际语言差异:

  • struct默认为公共访问,class默认为私有访问。
  • 继承时,struct默认为public继承,class默认为private继承。(具有讽刺意味的是,与C ++中的许多事情一样,默认值是向后的:public到目前为止,继承是更常见的选择,但是人们很少声明structs只是为了省去键入“ public”关键字。

但在实践中,真正的差别是间class/ struct声明构造函数/析构函数不和的。对于“普通数据” POD类型有某些保证,一旦您接管了班级的建设,这些保证将不再适用。为了清楚地区分,许多人故意将structs用于POD类型,并且,如果他们要添加任何方法,请使用classes。否则,以下两个片段之间的区别是没有意义的:

class X
{
  public:

  // ...
};

struct X
{
  // ...
};

(顺便说一下,这里的一个线程对“ POD类型”的实际含义做了一些很好的解释:C ++中的POD类型是什么?


关于继承差异的漂亮示例:此处
Liran Orevi 2010年

8
使用struct还是class不影响对象是POD还是应定义副本构造函数/析构函数。成员函数也与POD无关。当我重新阅读您写的内容时,我发现您没有其他建议,但当前的措辞令人困惑
David Stone

1
@DavidStone基本上,应该保证POD结构与C代码向后兼容,因此,基本上应该将POD结构设计为C样式结构。
贾斯汀时间-恢复莫妮卡

3
这个答案的“真正的区别”部分是完全错误的。
juanchopanza

177

现有答案中存在很多误解。

双方classstruct声明一个类。

是的,您可能必须在类定义中重新排列访问修改关键字,具体取决于您用来声明该类的关键字。

但是,除了语法之外,唯一选择一个的另一个原因是约定/样式/首选项。

有些人喜欢struct为没有成员函数的类使用关键字,因为生成的定义“看起来像”来自C的简单结构。

同样,有些人喜欢将class关键字用于具有成员函数和private数据的类,因为它上面写有“ class”,因此看起来像是他们最喜欢的面向对象编程书中的示例。

现实情况是,这完全取决于您和您的团队,并且对您的程序几乎没有任何影响。

以下两个类在名称上在各个方面都是绝对等效的:

struct Foo
{
   int x;
};

class Bar
{
public:
   int x;
};

您甚至可以在重新声明时切换关键字:

class Foo;
struct Bar;

(尽管由于不符合,这会中断Visual Studio的构建,所以当您这样做时,编译器将发出警告。)

并且以下表达式均计算为true:

std::is_class<Foo>::value
std::is_class<Bar>::value

不过请注意,重新定义时不能切换关键字;这仅仅是因为(按照一定义规则)跨翻译单元的重复类定义必须“由相同的令牌序列组成”。这意味着你甚至无法交流const int member;int const member;,并有无关的语义classstruct


15
这非常有用,说实话是我最喜欢的答案。每个人都将它们视为独立的实体,当在引擎盖下时,它们是相同的。我不知道Arduino环境中的结构定义是否被视为cpp类,因为它是使用g ++编译的。
本杰明

4
@Ben当然是。Arduino编译器可编译C ++;到此为止。
underscore_d

6
因此,如果我理解这一权利:只要您的可见性修饰符是显式的,而不是被忽略,那就没关系。您可以使用结构只编写一个庞大的C ++应用程序。否则您可以将每个struct结构更改为类。只要您不使用默认可见性,应用程序都将完全相同。
Ryan Lundy

我不认为C ++程序的一个翻译单元可以具有完整的声明,class foo { public: ... };而另一个可以具有struct foo { ... };根据“绝对等效”的说法必须成立的声明。原因是不完整的声明struct foo;class foo;是可以互换的。这些没有指定类主体,因此它们对访问布局一无所知。
哈兹

@Kaz:您是对的-如果定义在字面上是相同的类型,则它们在词汇上必须相同,这样行为才能得到明确定义。的结构键类键是否则逻辑可交换虽然(其中语义不受影响),和FooBar依然当量/相同类型。我确实确保说“在重新声明时并举一个例子。想一想,我会在答案中阐明这一点,以确保我不会误导人们进入UB
轨道轻轨赛

54

我唯一使用结构而不是类的方法是在函数调用中使用函子之前声明函子,并且为了清楚起见,希望最小化语法。例如:

struct Compare { bool operator() { ... } };
std::sort(collection.begin(), collection.end(), Compare()); 

35
现在已经过去了几年,并且所有主要的编译器都支持C ++ 11,Lambdas使其更加简洁

36

C ++ FAQ Lite

结构的成员和基类默认情况下是公共的,而在类中,它们默认是私有的。注意:应将基类显式设置为公共,私有或受保护,而不要依赖默认值。

struct和class在功能上是等效的。

好的,足够的干净利落的技术讨论。从情感上讲,大多数开发人员在类和结构之间做出强烈区分。结构只是感觉像是一堆开放的位,几乎没有封装或功能性的方式。班级感觉像是一个有活力的社会成员,拥有智能的服务,强大的封装障碍和明确定义的界面。因为这是大多数人已经拥有的含义,所以如果您有一个方法很少且具有公共数据的类(这种情况确实存在于设计良好的系统中!),则应该使用struct关键字,否则,您可能应该使用该类关键词。


1
我不明白为什么他们要说明struct和class在功能上是相同的,但是说在某些情况下在没有任何理由的情况下更喜欢struct和class
。.– deetz

3
原因是惯例。编译器不在乎您使用哪个编译器,但是另一位查看您的代码的开发人员将更容易理解您的意思。
Tal Pressman

3
@deetz:整个第三段都是推理。
Lightness Races in Orbit

哇,来自“老派”,我不知道一个结构可以有方法甚至可以继承。我只在没有方法的情况下仅使用数据时才使用struct,通常是在处理需要该结构的API代码时使用。结构也可以支持多重继承吗?
Paul McCarthy

21

结构对我有用的一个地方是当我的系统正在从另一个系统接收固定格式的消息(例如,串行端口)时。您可以将字节流转换为定义字段的结构,然后轻松访问这些字段。

typedef struct
{
    int messageId;
    int messageCounter;
    int messageData;
} tMessageType;

void processMessage(unsigned char *rawMessage)
{
    tMessageType *messageFields = (tMessageType *)rawMessage;
    printf("MessageId is %d\n", messageFields->messageId);
}

显然,这与您在C中所做的相同,但是我发现必须将消息解码为类的开销通常是不值得的。


同样可以在C.实现
尤金Bujak

11
或者您也可以实现operator >>一类,而不是写的processMessage功能,这将使你的C ++看起来更像适当的C ++而不是像C.
尼克·巴斯汀

1
除了不能在不同系统之间移植之外,这还违反了别名规则,因此即使在单个体系结构中也无法保证它可以工作。
underscore_d

@underscore_d这可以独立于平台(但不能用于编译器)工作,但是您必须使用固定位域,__packed__结构并具有endian感知的ifdef实现(您需要在字节序与外部数据源相反的计算机上翻转顺序)正在提供)。它不是很漂亮,但是我习惯于以可移植的方式打包/解压嵌入式平台上远程外围设备的寄存器。
crasic

1
@jacwah呵呵,很好。别名在这里不会有问题,因为指针之一是char类型,可以免于别名。但是,尽管存在一个不同的问题,但仍然存在一个问题:char*如果确实没有在该地址处初始化后一种类型的任何对象,则将a 强制转换为另一种类型是UB,因为它违反了生命周期规则。Afaik,即使目标类型是微不足道的可构造的,仅分配内存也不足以使C ++正式允许将该内存视为该类型。
underscore_d

19

如果要编写内部结构为C ++的库,则可以在C ++中使用“结构”,但是可以用C或C ++代码调用该API。您只需制作一个包含结构体和全局API函数的标头,就可以像下面这样对C和C ++代码公开这些标头:

// C access Header to a C++ library
#ifdef __cpp
extern "C" {
#endif

// Put your C struct's here
struct foo
{
    ...
};
// NOTE: the typedef is used because C does not automatically generate
// a typedef with the same name as a struct like C++.
typedef struct foo foo;

// Put your C API functions here
void bar(foo *fun);

#ifdef __cpp
}
#endif

然后,您可以使用C ++代码在C ++文件中编写功能bar(),并使其可从C调用,并且两个世界都可以通过声明的结构共享数据。当混合使用C和C ++时,当然还有其他注意事项,但这是一个简化的示例。


2
最合适的答案。C兼容性确实是最重要的原因。其他所有东西(例如默认访问权限)都是深奥的。
瓦伦丁·海尼兹

16

众所周知,唯一的区别是默认访问权限。但是,当我不想使用简单的数据类进行任何封装时,即使我实现了一些辅助方法,我也特别使用struct。例如,当我需要这样的东西时:

struct myvec {
    int x;
    int y;
    int z;

    int length() {return x+y+z;}
};

1
+1给出一个带有一些成员函数的结构的示例,这些成员函数看起来并不像您“拿走了结构”。
einpoklum

9

要回答我自己的问题(无耻地),正如已经提到的那样,访问特权是C ++中它们之间的唯一区别。

我倾向于仅将结构用于数据存储。如果它使处理数据更加容易,我将允许它获得一些帮助功能。但是,一旦数据需要流控制(即保持或保护内部状态的吸气剂/设置剂)或开始获取任何主要功能(基本上更像对象),它将被“升级”到一个类中以更好地传达意图。


9

当您要提供具有C ++实现的C兼容接口时,结构(POD,通常来说)比较方便,因为它们可以跨语言边界和链接器格式移植。

如果您不必担心,那么我认为使用“ struct”而不是“ class”可以很好地传达意图(如@ZeroSignal所述)。结构还具有更可预测的复制语义,因此它们对于打算写入外部媒体或通过网络发送的数据很有用。

结构对于各种元编程任务也很方便,例如仅公开一堆依赖的typedef的特征模板:

template <typename T> struct type_traits {
  typedef T type;
  typedef T::iterator_type iterator_type;
  ...
};

...但这实际上只是利用了struct的默认保护级别是公开的...


1
那不是POD的正确用法。一个结构(或类)在(且仅当)仅包含POD成员时才可以是POD结构。
马丁·约克

4
“可预测的复制语义”:语义与类相同(并且具有相同的问题(浅复制))。
马丁·约克2009年

2
这篇文章会让您相信(希望是偶然的)所有结构都是POD。这根本不是真的。我希望人们不要对此误导。
Michael Dorst 2014年

8

对于C ++,结构和类之间确实没有太大区别。主要的功能差异在于,结构的成员默认情况下是公共的,而它们在类中默认情况下是私有的。否则,就语言而言,它们是等效的。

也就是说,我倾向于像在C#中一样在C ++中使用结构,这与Brian所说的相似。结构是简单的数据容器,而类用于除保持数据外还需要对数据进行操作的对象。


4

他们几乎是同一回事。得益于C ++的魔力,结构可以像类一样保存函数,使用继承,使用“ new”创建等等。

唯一的功能差异是,类从私有访问权限开始,而结构从公共访问权限开始。这是保持与C的向后兼容性。

实际上,我一直将结构用作数据持有人,将类用作对象。


4

正如其他人指出的

  • 除默认可见性外,两者均等效
  • 无论出于何种原因,都有可能被迫使用一个或另一个

Stroustrup / Sutter明确建议何时使用:

如果类具有不变性,则使用class;如果数据成员可以独立变化,则使用struct

但是,请记住,向前声明sth是不明智的。作为类(class X;)并将其定义为struct(struct X { ... })。它可能在某些链接器(例如,g ++)上起作用,而在其他链接器(例如,MSVC)上可能失败,因此您会发现自己陷入了开发人员的泥潭。


您能描述这些链接器问题吗?
Lightness Races in Orbit,

不幸的是,我不能@LightnessRacesinOrbit。我什至无法提出一个例子。琐碎的class Foo; struct Foo { void bar() {} }; int main() { Foo().bar(); }事情不仅会编译并与MSVC 2017一起运行,它甚至还会产生明确的警告,该警告Foo已声明为struct但定义为class。但是我也清楚地记得,我们的团队花了半天的时间才发现该愚蠢的错误。我不确定当时使用的MSVC版本。
pasbi '19

链接器甚至不应该知道您使用了class还是struct在前向声明上,并且按照标准,两者可以自由互换(尽管众所周知VS警告;我一直认为那只是为了避免明显的程序员错误)。这里没有什么味道。您确定这不是违反ODR的错误吗?
Lightness Races in Orbit


我不记得了。该问题不是直接在应用程序中发生的,而是在gtest中使用的库中发生的。我只知道链接器产生了无法理解的错误(LNK ???)。一旦我将struct-forwards 替换为class-forwards,问题就消失了。从今天起,我也觉得很奇怪。如果您能对此有所了解,我会很高兴。
pasbi '19

3

类。

默认情况下,班级成员是私人的。

class test_one {
    int main_one();
};

相当于

class test_one {
  private:
    int main_one();
};

所以如果你尝试

int two = one.main_one();

我们将收到一个错误:main_one is private因为它不可访问。我们可以通过指定一个public来初始化它来解决它

class test_one {
  public:
    int main_one();
};

结构。

struct是一类,默认情况下成员是公共的。

struct test_one {
    int main_one;
};

手段main_one是私人的,即

class test_one {
  public:
    int main_one;
};

我将结构用于成员可以取任何值的数据结构,这样比较容易。


3

structover 的优点class是,如果坚持“先公开成员,然后私有”,那么它将节省一行代码。因此,我发现关键字class没有用。

这是仅使用struct永不使用的另一个原因class。一些C ++的代码风格指南建议对函数宏使用小写字母,其基本原理是,当宏转换为内联函数时,无需更改名称。同样在这里。您拥有了不错的C样式结构,有一天,您发现需要添加一个构造函数或一些便捷方法。您将其更改为class吗?到处?

区分structs和classes太麻烦了,进入了做我们应该做的事情的方式-编程。像许多C ++问题一样,它源于对向后兼容性的强烈渴望。


为什么需要将其更改为class?您是否认为用struct关键字定义的类不能具有成员函数或构造函数?
Lightness Races in Orbit

@LightnessRacesinOrbit由于1.一致性和2.一些静态分析器抱怨违反1
.。– Vorac

那不会。您还坚持其他哪些“一致性”?每个带有成员调用的类都joe()必须用class关键字定义?每个至少有4位int成员的课程都必须使用struct关键字定义?
Lightness Races

@LightnessRacesinOrbit我指的是成语“ POD聚合定义为struct,带方法聚合定义为class”。太麻烦了。
Vorac

2

它们是相同的东西,具有不同的默认值(默认情况下为private,默认情况下为classpublic struct),因此从理论上讲它们是完全可以互换的。

因此,即使我只想打包一些信息来移动,我也会使用一个结构,即使我在其中放置了一些方法(但不是很多)。如果是最不透明的东西,主要用途是通过方法,而不是直接传递给数据成员,则使用完整类。


2

默认情况下,结构具有公共访问权限,默认情况下,类具有私有访问权限。

我个人将结构用于数据传输对象或用作值对象。当这样使用时,我将所有成员声明为const,以防止被其他代码修改。


2

这两个structclass是引擎盖下一样,虽然有不同的默认值,以知名度,struct缺省为public,class默认为私有。您可以使用private和适当地将其中一个更改为另一个public。它们都允许继承,方法,构造函数,析构函数以及面向对象语言的所有其他优点。

但是,两者之间的巨大差异struct是C支持关键字,class而不支持C。这意味着,可以使用一个struct包含文件中,可以是#include进入或者C ++或C,只要struct是普通的C风格struct,并在一切包括文件兼容C,即没有C ++特定的关键字,例如privatepublic无方法,没有继承等,等等。

AC风格struct可以与其他支持C风格的接口一起使用,struct以在接口上来回传输数据。

AC样式struct是一种描述内存区域布局的模板(不是C ++模板,而是一种模式或模具)。多年以来,已经创建了可用于C和C插件的接口(在这里为您介绍Java和Python和Visual Basic),其中一些可用于C样式struct


1

从技术上讲,两者在C ++中是相同的-例如,结构可能有重载的运算符等。

但是:

当我希望同时传递多种类型的信息时,我使用结构体;当我处理“功能”对象时,我使用类。

希望能帮助到你。

#include <string>
#include <map>
using namespace std;

struct student
{
    int age;
    string name;
    map<string, int> grades
};

class ClassRoom
{
    typedef map<string, student> student_map;
  public :
    student getStudentByName(string name) const 
    { student_map::const_iterator m_it = students.find(name); return m_it->second; }
  private :
    student_map students;
};

例如,我在这里的get ...()方法中返回一个struct学生-享受。


1

什么时候选择使用struct,何时选择使用C ++中的类?

struct在定义functors和时使用POD。否则我用class

// '()' is public by default!
struct mycompare : public std::binary_function<int, int, bool>
{
    bool operator()(int first, int second)
    { return first < second; }
};

class mycompare : public std::binary_function<int, int, bool>
{
public:
    bool operator()(int first, int second)
    { return first < second; }
};

1
这个答案显示出年龄的迹象:) std::binary_function<>不仅被弃用,c ++ 17甚至将其删除。
sehe

因为它是在15年前定义的C ++ 03时代编写的,所以这并不令人感到意外。
Lightness Races in Orbit

1

当我需要创建POD类型或函子时,我使用结构。


1

默认情况下,所有类成员都是私有的,默认情况下,所有结构成员都是公共的。类具有默认的私有基础,而Struct具有默认的公共基础。在C的情况下,结构不能具有成员函数,而在C ++的情况下,我们可以将成员函数添加到结构中。除了这些差异之外,我没有发现任何令人惊讶的地方。


0

仅当我需要保留一些没有任何关联成员函数的数据(对成员数据进行操作)并直接访问数据变量时,才使用struct。

例如:从文件和套接字流等读取/写入数据。在结构中传递函数参数,在该结构中函数参数太多而函数语法看起来太长。

从技术上讲,除了默认可访问性之外,类和结构之间没有太大区别。它的更多内容取决于编程风格,以及如何使用它。


-4

我认为Structs旨在作为一种数据结构(如信息的多数据类型数组),而类是为代码打包而设计的(如子例程和函数的集合)。

:(


-6

我从不在C ++中使用“结构”。

我无法想像一个场景,当您想要私有成员时会使用struct,除非您故意要造成混淆。

似乎使用structs更像是如何使用数据的语法指示,但我宁愿仅创建一个类并尝试以类的名称或通过注释将其显式表示。

例如

class PublicInputData {
    //data members
 };

据我说,“语法上如何使用数据的指示”是使用结构的一个很好的理由,特别是如果替代方法是在类名中使用注释或名称。
维克多·塞尔

2
会不会struct很明确地宣布该类的成员默认情况下将是公共的?
错误的用户名2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.