Jump to content
  • Advertisement
Sign in to follow this  
RoundPotato

Object tries to eat potatoeses

This topic is 2114 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Advertisement

The fourth line creates an unnamed temporary object, assigns it to objs[2], and then the temporary object is destroyed. That's the destructor you're seeing. If this messes up the object being assigned to, then your class does not properly implement its assingment operator to copy the resources.

 

But also, you're not assigning to a valid object. None of the objects in objs have been constructed, and assignment assumes you're assigning to an already constructed object. You have to properly construct the objects after allocating the memory; if you insist on allocating the memory with malloc, then you can construct the objects in the memory buffer with placement new.

Share this post


Link to post
Share on other sites

With your example of having a stuct with two variables 8B and 2B , you suggest aligning it to 8B, I would assume the structure would need 16B then ? How would that also work out if sizeof(exampleStruct) return 10B then ? would each member require a minimum of 8B if it's actual size is below?

When you create an array of structures, they're spaced sizeof(structure) apart. If the struct requires 8B alignment (because it has an 8B member), but the size is 10B, then the first element will be aligned correctly, but the 2nd/3rd/etc elements in the array won't be aligned correctly.
To simplify this, the compiler will insert padding into structures, making them bigger than they need to be.

e.g. Try running this code:

struct Example { long long eightBytes; short twoBytes; };
printf( "%d", sizeof(Example) );

It should print 16, not 10! The compiler inserts 6 "padding" bytes after the last member, to ensure that the size of the struct is alignable. The first member requires an alignment of 8, the second member requires an alignment of 2.
As long as the start of the array is aligned to a multiple of 8 (or 16, or 32...), then every element in the array will also be correctly aligned, and every member will be correctly aligned.
e.g. say the array starts at address #48 -- array[0].eightBytes is at #48, a multiple of 8, and array[0].twoBytes is at #56, a multiple of 2. array[1].eightBytes is at #64, a multiple of 8, and so on.
 

Also how would you actually do the aligning ?

It used to be fairly complicated, so I would've suggested just using an alignment of 16 for everything. Since C++11 though, as well as sizeof(T), we also now have alignof(T).

e.g. for MS Visual Studio, you can use the aligned malloc/free functions like this:

template<class T>
T* AllocArray( int count )
{
	return (T*)_aligned_malloc( sizeof(T)*count, alignof(T) );
}
void FreeArray( void* ptr )
{
	_aligned_free( ptr );
}


Example* myArray = AllocArray<Example>( 5 );

new(&myArray[2]) Example();

myArray[2].~Example();

FreeArray( myArray );

[edit]Instead of using MSVC-specific functions, the portable C++11 version of the above is:

template<class T>
T* AllocArray( int count )
{
	return (T*)aligned_alloc( alignof(T), sizeof(T)*count );
}
void FreeArray( void* ptr )
{
	free( ptr );
}

"malloc" aligns by default with 8 bytes or something similar, any possible confirmations ?

Yes, (IIRC...) this is true -- malloc will always return an allocation that is correctly aligned for any standard primitive type, which in practice, means it's aligned to 8 bytes.

However, there are also non-standard primitive types, such as __m128 which is an SSE variable representing 4 floats stored together, and has a size/alignment of 16-bytes. That's why I recommend using 16 bytes as the "default" alignment value, not 8 bytes as the standard recommends... wacko.png

 

The above example of alignof(T) always selects the correct alignment though, so you don't have to worry about picking a good default one-size-fits-all

Edited by Hodgman

Share this post


Link to post
Share on other sites

You should always follow the rule of three when writing C++ code though. At the very least, if your class has a destructor, but you don't want to implement copying/assignment, then you must declare them as being private in order to prevent the compiler from automatically implementing them

Or, in C++11, you'd explicitly 'delete' them; and if you want to show others that you really desire default-generated functions, then you can explicitly mark them as 'default'.
 

class SomeObjectInCpp11
{
public:
  SomeObject() = default; //Have the compiler generate it.
  SomeObject( const SomeObject& ) = delete; //Make sure others can't use it.
  SomeObject& operator=( const SomeObject& ) = delete;
  ~SomeObject(); //Define it yourself
  
  //For objects managing data, these are also useful to implement
  //(usually as a swap of the data pointers, rather than copying the data).
  SomeObject( SomeObject&& ); //Move-constructor
  SomeObject& operator=(SomeObject&&); //Move-assignment operator
};

People joke that in C++11 the 'rule of three' is now the 'rule of five'. While not a strict requirement, the two additional functions can speed up the code using the class by a great deal, and is usually easy to implement.

Rule of Three in C++ - Wikipedia

Share this post


Link to post
Share on other sites

^ This. I'm not seeing yet why you're specifically avoiding 'new' (unless you're doing it for learning purposes, in which case do what you will :D ).

Edited by TheChubu

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!