C++:结构体和类真的一样吗?

4
这是一个重复的问题《C++中struct和class的区别在哪里?》,但有时我会遇到一些令人困惑的事情。
总的来说,C++结构体和类的区别在于默认的 public vs. private 访问。除此之外,C++ 编译器对待结构体的方式与它对待类的方式相同。结构体可以拥有构造函数、拷贝构造函数、虚函数等等。并且结构体的内存布局与类的内存布局相同。C++ 之所以有结构体是为了向后兼容 C。
现在,因为人们不确定使用 struct 还是 class,经验法则是如果你只有普通的数据,请使用 struct。否则使用 class。我读到过结构体在序列化方面很好,但不知道这是从哪里得出的。

我最近看到了这篇文章:http://www.codeproject.com/Articles/468882/Introduction-to-a-Cplusplus-low-level-object-model

文章说,如果我们有以下代码(直接引用):

struct SomeStruct
{ 
    int    field1;
    char   field2;
    double field3;
    bool   field4; 
};  

然后是这个:
void SomeFunction()
{
    SomeStruct someStructVariable;
    // usage of someStructVariable
    ... 
}  

和这个:

void SomeFunction()
{ 
    int    field1;
    char   field2;
    double field3;
    bool   field4;      
    // usage of 4 variables
    ... 
}

它表示如果我们有一个结构体或者只是在函数内写下变量,生成的机器代码是相同的。当然,这仅适用于您的结构体是POD的情况。

这就是我感到困惑的地方。在Effective C++中,Scott Meyers说没有空类的概念。

如果我们有:

class EmptyClass { };

实际上,它是由编译器布局的,例如:
class EmptyClass
{
    EmptyClass() {}
    ~EmptyClass() {}
    ...
};

所以您不会有一个空的类。

现在,如果我们将上述结构体更改为类:

class SomeClass
{ 
    int  field1;
    char field2 
    double field3;
    bool  field4; 
}; 

这是什么意思:

void SomeFunction()
{
    someClass someClassVariable;
    // usage of someClassVariable
    ... 
}  

和这个:

void SomeFunction()
{ 
    int  field1;
    char field2 
    double field3;
    bool  field4;      
    // usage of 4 variables
    ... 
}

这些在机器指令方面是相同的吗?没有调用someClass构造函数吗?还是分配的内存与实例化类或单独定义变量相同?那填充呢?结构体和类会填充。在这些情况下,填充是否相同?我真的很感激如果有人能为此提供一些见解。

从技术上讲,struct 的默认继承访问权限是 public,而 class 则为 private - chris
4
如果你从阅读那篇文章中得出的印象是 structclass 在除默认成员访问保护级别外有任何不同,那么你的印象是错误的。就这么简单。 - Seth Carnegie
-1:由于缺乏研究,我只在“提问”标题框中输入了您问题的标题。而在相关问题列表中,第一个问题(当然是在这个问题之后)就是一个(已关闭的)问题,它引导了答案。那个带有相关问题列表的框不仅仅是为了好看。 - Nicol Bolas
@NicolBolas 谢谢。我已经做了研究。我已经多次阅读了那个问题。然而,我的问题是关于上述文章和我感到的可能矛盾之处。我问了关于构造函数、填充和汇编代码的问题。如果你读了这个问题,你会看到我已经提到了公共 vs. 私有。我的问题是关于编译器生成的代码,这在那个问题中没有讨论。我建议你在输入我的标题到“提问”之前先阅读完整个问题。请阅读这个问题的一些答案,你就会明白了。干杯。 - madu
4个回答

2
唯一的区别在于结构体的成员默认为公共的,而类的成员默认为私有的(“默认情况下”是指“除非另有规定”)。请查看以下代码:
#include <iostream>
using namespace std;

struct A {
 int x;
 int y;
};

class A obj1;

int main() {
  obj1.x = 0;
  obj1.y = 1;
  cout << obj1.x << " " << obj1.y << endl;
  return 0;
}

代码编译和运行都很顺利。

1
你忽略了一个事实,即结构体基类默认为公有,而对于类来说则是私有的。 - juanchopanza

2
我认为那篇文章的作者是错误的。虽然这两个函数的结构体和非成员变量布局版本可能没有区别,但我不认为这是有保证的。我所能想到的唯一保证是,由于它是POD,结构体和第一个成员的地址相同...每个成员在某个时刻之后在内存中跟随其后。
在任何一种情况下,由于它是POD(类也可以是,不要犯这个错误),数据都不会被初始化。
我建议不要做出这样的假设。如果你编写了利用它的代码,我无法想象为什么你要这么做,大多数其他开发人员也会感到困惑。除非必须这样做,否则最好遵循人们习惯的编码方式。所有这些中真正重要的部分是,除非你明确地这样做,否则POD对象不会被初始化。

1

结构体和类之间除了默认保护类型没有区别(注意基类的默认保护类型也不同),这是书籍和我自己20多年的经验告诉我们的。

关于默认空构造函数/析构函数。标准并没有要求这样做。然而,一些编译器可能会生成这对空的构造函数/析构函数。任何合理的优化器都会立即将它们丢弃。如果在某个地方调用了一个什么也不做的函数,你如何检测到这个问题?除了消耗CPU周期外,这会对任何事情产生影响吗?

MSVC不会生成无用的函数。可以合理地认为每个好的编译器都会这样做。

关于这些例子:

struct SomeStruct
{ 
    int    field1;
    char   field2;
    double field3;
    bool   field4; 
}; 

void SomeFunction()
{ 
    int    field1;
    char   field2;
    double field3;
    bool   field4;      
   ... 
}

填充规则、内存中的顺序等可能会完全不同,而且很可能会完全不同。优化器可以轻松地丢弃未使用的局部变量。优化器几乎不可能(如果可能的话)从结构体中删除数据字段。要发生这种情况,结构体应该在cpp文件中定义,应设置某些标志等。

我不确定您是否会找到有关堆栈上局部变量填充的文档。据我所知,这完全取决于编译器制作此布局。相反,结构/类的布局已经描述,有#pargma和命令行键来控制此操作等。


1
“从机器指令的角度来看,它们是否相同?” “理论上是没有问题的,但标准不能保证。” “这里面没有调用someClass构造函数吗?” “是的,有一个对构造函数的调用。但是构造函数不起作用(因为所有成员都是POD,并且您声明的方式 someClass someClassVariable; 引起了值初始化,该操作对POD成员无效)。因此,既然没有工作要做,就没有必要安排任何指令。” “内存分配是否与实例化类或单独定义变量相同?” “类中可能包含填充,而单独声明变量则没有。另外,我确信编译器会更容易地优化单独的变量。” “填充呢?” “结构(struct/class)中可能存在填充。” “struct和class都有填充。在这些情况下,填充是否相同?” “是的,请确保你在比较两者时不要混淆。”
struct SomeStruct
{ 
    int    field1;
    char   field2;
    double field3;
    bool   field4; 
}; 
class SomeStruct
{ 
  public:           /// Make sure you add this line. Now they are identical.
    int    field1;
    char   field2;
    double field3;
    bool   field4; 
}; 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接