Reputation: 3672
I have the following class and its alignment is 8 and the size is 40 bytes (compiled using gcc v11.2 64-bit).
class Foo
{
public:
inline Foo( ) = default;
/*
other member functions such as
move ctor and move assignment operator etc.
*/
private:
mutable std::vector< std::vector<char> > _characterMatrix; // occupies 24 bytes on the stack
int _Y_AxisLen; // 4 bytes
int _X_AxisLen; // 4 bytes
char _fillCharacter; // 1 byte
inline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' }; // static member so nothing
};
And this statement:
std::cout << "Size: " << sizeof( Foo) << " --- alignment: " << alignof( Foo) << '\n';
generates this:
Size: 40 --- alignment: 8
The size of the class is actually 8(size member of vector obj) + 8(capacity member of vector obj) + 8(qword pointer to the memory block on the heap for vector obj) + 4(int) + 4(int) + 1(char). So 8+8+8+4+4+1 == 33
bytes. And 7 bytes are being wasted every time an instance of Foo is initialized.
So my question is that is there a way to decrease the size down to 36?
I also tried this:
class alignas( 4 ) Foo
{
public:
inline Foo( ) = default;
/*
other member functions such as
move ctor and move assignment operator etc.
*/
private:
mutable std::vector< std::vector<char> > _characterMatrix;
int _Y_AxisLen;
int _X_AxisLen;
char _fillCharacter;
inline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' };
};
But the compiler ignored the alignas(4). The size was still 40.
Edit: After reading some of the comments I noticed that in many STL implementations, a std::vector object does not have a pointer and a size and a capacity member but instead it has 3 pointers(each one 8 bytes in size so again 24 in total).
Upvotes: 0
Views: 153
Reputation: 62576
You can use bit-fields to control exactly how big your members are.
class Foo {
public:
Foo() = default;
~Foo() = default;
Foo(const Foo & other)
: matrix(std::make_unique_for_overwrite<char[]>(other.size())),
Y_AxisLen(other.Y_AxisLen),
X_AxisLen(other.X_AxisLen),
FillCharacter(other.FillCharacter) {
std::copy(other.matrix.get(), other.matrix.get() + size(), matrix.get());
}
Foo(Foo && other) = default;
Foo& operator=(Foo other) {
swap(matrix, other.matrix);
Y_AxisLen = other.Y_AxisLen;
X_AxisLen = other.X_AxisLen;
FillCharacter = other.FillCharacter;
return *this;
}
private:
static_assert(CHAR_BIT == 8, "Bit fields assume 8 bit char");
std::size_t size() const { return Y_AxisLen * X_AxisLen; }
// support [x][y] access
char* operator[](size_t col) const { return matrix.get() + (col * Y_AxisLen); }
std::unique_ptr<char[]> matrix;
std::uint64_t Y_AxisLen : 28;
std::uint64_t X_AxisLen : 28;
std::uint64_t FillCharacter : 8;
inline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' };
};
As a bonus, this has a size of 16 (but still an alignment of 8)
Upvotes: 2
Reputation: 304
class Foo
{
public:
inline Foo( ) = default;
private:
char ** _characterMatrix; // 8 bytes
int _Y_AxisLen; // 4 bytes
int _X_AxisLen; // 4 bytes
char _fillCharacter; // 1 byte
intline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' };
};
Upvotes: 0