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).
CodePudding user response:
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)
CodePudding user response:
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 { '/', '\\', '|', '-' };
};