The 2024 Wheel Reinvention Jam just concluded. See the results.

"class" vs "struct" in C++

I've seen many instances where people say that "class" and "struct" are the same. But when testing initializing both the the "new" keyword in C++, it looks that they behave differently. When you create an instance of a "class" with "new", it also initialized its members to "0", similar to what "{}" does to structs. But what I replace "class" with "struct" for the same data structure and keep the same initialization with "new", I can an instance where members of it have garbage data.

Is that a feature of C++ and is that compiler specific? (compiling with Visual Studio). If there is such a difference between the two ways of declaring a data structure, why do people say that "class" is no different from "struct" in C++. Even though the difference is in how it gets initialized, you can't just replace your classes with structs and assume it will work the same, because that is not the case. Maybe I am missing something?
den_v
When you create an instance of a "class" with "new", it also initialized its members to "0", similar to what "{}" does to structs.

No, it does not. Well, not always. I depends on multiple things for this to happen.
Disclaimer: all stuff written below is for C++03 and newer C++ standards. For C++98 the behavior is a bit different. But nobody uses C++98 anymore, so lets ignore it.

Here's an example where you can see that struct and class behaves exactly the same for member initialization:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

class Class { public: int x; };
struct Struct { int x; };

int main()
{
  Class* c = new Class;
  printf("%i\n", c->x);

  Struct* s = new Struct;
  printf("%i\n", s->x);
}

And when I compile and execute it:
1
2
3
4
5
C:\test>g++ test.cpp -o test

C:\test>test.exe
38675600
14312016

You may be lucky and compiler has initialized heap memory with 0 or placed both objects in place where memory contents is 0, then of course you won't see random values like here. But you cannot rely on that. At some point compiler will start placing objects on heap that reuses previously used memory which contains non-zero values and then you'll see garbage values like the example above.

struct and class differs only in two properties - default inheritance for struct is public, and default visibility for members is also public. There are no other differences between struct and class. Everything else is the same. Both can have construtors/destructors, both can be inherited in whatever way you want, both can have virtual or pure virtual functions. Both can be used as types for templates or whatever else.

You can get 0-initialized struct and class POD members if you have auto-generated constructor and make new operator to use it:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

class Class { public: int x; };
struct Struct { int x; };

int main()
{
  Class* c = new Class(); // <-- this will make compiler to use auto generated constructor
  printf("%i\n", c->x);

  Struct* s = new Struct(); // <-- this will make compiler to use auto generated constructor
  printf("%i\n", s->x);
}

Compile and run it:
1
2
3
4
5
C:\test>g++ test.cpp -o test

C:\test>test.exe
0
0

But if you have an explicit constructor, then again no POD types will be initialized automatically:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

class Class {
public:
  Class() {} 
  int x;
};

struct Struct {
  Struct() {} 
  int x;
};

int main()
{
  Class* c = new Class();
  printf("%i\n", c->x);

  Struct* s = new Struct();
  printf("%i\n", s->x);
}

Compile and run it:
1
2
3
4
5
C:\test>g++ test.cpp -o test

C:\test>test.exe
7349392
7234128

Btw this has nothing to do with "new", same thing will happen when you place objects on stack:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main()
{
    // assume Struct and Class are without explicit constructor, like in examples nr1 and nr2 above

    Class c1{}; // cannot use () because compile will think this is function declaration
    Class c2;
    Struct s1{};
    Struct s2;

    // now c1.x and s1.x will be 0
    // but c2.x and s2.x will contain garbage
}

Edited by Mārtiņš Možeiko on
mmozeiko

You may be lucky and compiler has initialized heap memory with 0 or placed both objects in place where memory contents is 0, then of course you won't see random values like here. But you cannot rely on that. At some point compiler will start placing objects on heap that reuses previously used memory which contains non-zero values and then you'll see garbage values like the example above.


Interesting, so what you are saying is that compiler treats code different based on whether it is struct or a class, even though it should behave the same? I can reproduce this behavior 100% of the time. Anytime I change "class" to "struct", I get garbage values instead of zeros. Then go back to "class" and values are init'd to zeros again.

I will now definitely write proper init function for my class/struct, I was just curious why it is that way.
I am not saying that. Generated machine code is exactly same for struct and class.

What I tried to say is that each call to new is different. For same type some calls may place object in memory where contents is already 0, but some calls may place object where memory is not 0. In case where you don't initialize members to 0, you'll will see garbage values, with small probability of them being 0.

I can reproduce this behavior 100% of the time. Anytime I change "class" to "struct", I get garbage values instead of zeros. Then go back to "class" and values are init'd to zeros again.
Can you show small code example where this happens?
This is the data structure that I swap between "class" and "struct":
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class polygon
{
public:
	polygon(int vertCount)
	{
		numOfVerticies = vertCount;
		vertices = new v2[vertCount];
	}
	~polygon()
	{
		delete[] vertices;
	}

	bool isVisible;
	
	float acceleration;
	int rotationDeltaDegrees;

	v2 position;
	float velocity;


	// TODO - this should probably be unit vector
	v2 movementDireciton;        // Direction in which teh polygon is moving

	int numOfVerticies;
	v2 *vertices;
};



And here is initialization:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void InitPlayer(game_state *state)
{
	state->player = new spaceship();
	state->player->projectileWeapon = v2(0, 35);
	state->player->model = new polygon(4);
	polygon *playerModel = state->player->model;

	playerModel->isVisible = true;

	SetPlayerModel(playerModel);

	state->player->facingDirection = GetInitPlayerDirection();
	playerModel->position = v2(500, 200);
	state->player->maxVelocity = 1.0;
}


If I breakpoint after this line
1
state->player->model = new polygon(4);

then if the structure was a "class", its members are init'd to zeros. But if a structure was a "struct", then members have garbage in them after the the "new" has been called.

The garbage manifested itself during rendering stage, where I look at the values for rendering math.
Then this is that "lucky" situation I was talking. Object got placed in memory location that already contains 0. It may happen to be first allocation in page that C++ runtime got from OS with VirtualAlloc (which always returns 0 initialized pages).

If I create simple standalone test, you can see that values won't be zero:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>

struct v2 { float x, y; };

class polygon
{
public:
	polygon(int vertCount)
	{
		numOfVerticies = vertCount;
		vertices = new v2[vertCount];
	}
	~polygon()
	{
		delete[] vertices;
	}

	bool isVisible;
	
	float acceleration;
	int rotationDeltaDegrees;

	v2 position;
	float velocity;


	// TODO - this should probably be unit vector
	v2 movementDireciton;        // Direction in which teh polygon is moving

	int numOfVerticies;
	v2 *vertices;
};


int main()
{
   polygon* p = new polygon(4);
   printf("%i\n", p->isVisible);
   printf("%i\n", p->rotationDeltaDegrees);
}


Compile & run it:
1
2
3
4
5
6
C:\test>cl -nologo test.cpp
test.cpp

C:\test>test.exe
97
103

Edited by Mārtiņš Možeiko on
I tried your code in Visual Studio 2015, debug build, and I get absolutely no difference if I use "struct" or "class" on the "polygon" data type, as expected. mmozeiko's description is spot on - the only difference between struct and class is that struct is public by default.

mmozeiko
But nobody uses C++98 anymore, so lets ignore it.
Actually, C++98 is still used a lot. There are many projects and companies where upgrading can take a long time, and may not even be an option. And then there's people who might chose not to upgrade, as the C++0x features won't make a difference to them. I agree that we can ignore it for this discussion, just wanted to point out that overall, it is far from irrelevant still :)
Sure, there is a lot of pre-C++03 code base out there. And there is nothing wrong with it.

But if you are using, for example, Visual Studio 2005 (or newer), then you have C++03 compliant compiler. There's no way to switch it to C++98 like you can with gcc and clang. My point here is that not a lot of people use very old versions of Visual Studio. So there's no need to discuss how your code will behave on C++98. I probably should have said "nobody *here* uses C++98 anymore, except @nothings" :)

Edited by Mārtiņš Možeiko on
I'm using Visual Studio 2003 :P But point taken :)
This was very helpful. Thank you all who contributed to the topic.