Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are class members guaranteed to be contiguous in memory?

Tags:

c++

I have a class which holds a number of function pointers and I would like for them all to be initialized to NULL when objects are constructed. To do this I was planning on using memset on the memory locations from the first pointer to that of the last pointer however, I am unsure if this would work 100% of the time.

Is it guaranteed that if these function pointers are declared contiguously within the class that their memory locations would also be contiguous. I assume that padding won't affect what I'm trying to do as any padding bytes would just be set to NULL also.

Example class implementation

class C
{
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};
like image 584
ctor Avatar asked Mar 15 '13 11:03

ctor


3 Answers

It is guaranteed that they appear with increasing addresses in the order declared. This is true in general of data members without intervening access specifiers, so if there are other data members in the class then the only way they could intervene is if there are access specifiers in there.

I don't think it's guaranteed to be safe to modify padding bytes. I don't think it's guaranteed that the implementation won't put "something important" in between data members, although I can't immediately think of anything an implementation would want to put in there. Type information for a strangely-designed accurate-marking GC? Recognizable values to test for buffer overruns?

It's not guaranteed that all-bits-zero represents a null function pointer.

You could deal with the issue of the all-bits-zero representation using something like:

std::fill(&func1, &func4 + 1, (void(*)(void))0);

but that would still leave the issue of padding. You're guaranteed no padding in an array, but not (by the standard) in a class. The ABI used by your implementation might specify struct layout to the degree necessary to ensure that your class above is laid out the same as an array of 4 function pointers.

An alternative is to do the following:

struct function_pointers {
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};

class C : private function_pointers
{
public:
    C() : function_pointers() {}
};

The initializer function_pointers() dictates that (since it doesn't have a user-declared constructor) the members of function_pointers are zero-initialized even if the instance of C itself is only default-initialized. function_pointers could be a data member rather than a base class, if you prefer to type a bit more to access func1 etc.

Note that C is now non-POD in C++03. In C++11 C remains standard-layout after this change, but would not be standard-layout if there were any data members defined in C, and it is not a trivial class. So if you were relying on POD/standard/trivial-ness then don't do this. Instead leave the definition of C as it is and use aggregate initialization (C c = {0};) to zero-initialize instances of C.

like image 151
Steve Jessop Avatar answered Sep 28 '22 01:09

Steve Jessop


Do this first.

class C
{
public:
    C() : func1(nullptr), func2(nullptr), func3(nullptr), func4(nullptr)
    { };
private:
    void (*func1)();
    void (*func2)();
    void (*func3)();
    void (*func4)();
};
like image 39
Alex Chamberlain Avatar answered Sep 28 '22 00:09

Alex Chamberlain


Never ever use memset on non-trivial objects, particularly on those which are polymorphic (having virtual functions or deriving from base class having virtual methods and so forth). If you do it, you would blow-up the vptr which points to vtable! A disaster!

vptr & vtable are used to implement polymorphic behavior, hence respect those hidden class members.

memset should be used when we talk in terms of bits and bytes, not when we talk about objects.

Respect non-trivial objects, say no to memset :)

like image 25
Arun Avatar answered Sep 28 '22 02:09

Arun