Container : when do I need a move constructor ?

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

Container : when do I need a move constructor ?

Post by binbinhfr » March 31st, 2020, 2:06 pm

Hi,

let say I have

Code: Select all

class B {....};

class A
{
	A() { pb = new(B); };
	~A { delete pb; }
	
	B* pb = nullptr;
};

std::vector<A> veca;
The A class has a default trivial copy constructor and a default trivial move constructor, right ? They are just copying the pb pointer, without any memory reallocation, right ?
As I do not need the copy constructor, to avoid any error of 2 pointers pointing on the same allocated memory, I want to delete the copy constructor.

Code: Select all

A(const A&) = delete;
But then it won't compile.
It seems that it also delete the default move constructor,
Because if I add :

Code: Select all

A(const A&&) = default;
It compiles again and seems to work properly.

But is it safe ? I suppose it is, but I would like a confirmation from the specialists...

So final result :

Code: Select all

class B {....};

class A
{
	A() { pb = new(B); };
	~A { delete pb; }
	A(const A&) = delete;
	A(const A&&) = default;
	
	B* pb = nullptr;
};

std::vector<A> veca;

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Container : when do I need a move constructor ?

Post by albinopapa » March 31st, 2020, 3:56 pm

Actually it's the declaring and defining of the destructor. Since you declared a destructor, the only implicit constructor is the copy and maybe the default constructor. Since you deleted the copy constructor, there was no way to assign one A to another. It's good that you are deleting the copy constructor if you aren't going to do a deep copy. Honestly, what I would do is use std::unique_ptr instead of raw pointers with new/delete. The reason being aside from being "safer", you also don't have to define a destructor. This prevents the class from being default copyable and still allows for it to be default movable.

Code: Select all

class B {....};

class A
{
public:
	A() : pb( std::make_unique<B>() ) {};

	std::unique_ptr<B> pb;
};

std::vector<A> veca;
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Container : when do I need a move constructor ?

Post by albinopapa » March 31st, 2020, 4:06 pm

The move constructor is only useful for classes that hold resources, like the new/delete raw pointers or file pointers. Usually, it's for transferring ownership from one object to another. When one object moves it's resource to another, the first object sets that resource to some invalid but reusable state. In the case of a raw pointer allocated with new/delete you'd set the lending pointer to nullptr instead of freeing it with delete.

Other members can be set to some default or invalid state signifying that the object should not be used. Move semantics, through the use of the move constructor or move assignment operator doesn't have any benefit over copying if you only have concrete data types ( as opposed to heap or other allocated resources ).

For instance, you cannot move a C array, it just gets copied.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

Re: Container : when do I need a move constructor ?

Post by binbinhfr » March 31st, 2020, 4:32 pm

Honestly, what I would do is use std::unique_ptr instead of raw pointers with new/delete.
ok i'll have a look at it. (my old C habits... ;-) )
The move constructor is only useful for classes that hold resources, like the new/delete raw pointers or file pointers.
Yes, that's what I understood. And my class A do have a B pointer, so it is concerned, no ?

I do not really understand why defining a destructor implies a constructor that is by default empty anyway ?
Don't classes always have default constructors/destructors with empty body even if we do not specify them ?

What if members are initialized inline without constructors specification like

Code: Select all

class A{
  int x = 1;
}

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Container : when do I need a move constructor ?

Post by albinopapa » March 31st, 2020, 5:57 pm

If a destructor is declared, even if declared as = default it prevents the compiler from defining default special member functions like the copy/move constructors and assignment operators. This is why it's best to define them all or define none.

It's even been suggested in some CppCon videos that you are better off making small utility classes that can handle resources that have all those special functions defined. Then just use the utility class in your regular classes to avoid having to define or even declare any of the special member functions.

This is why in the example I used with std::unique_ptr I was able to remove all other special functions such as the move/copy constructors and the destructor.

Classes will always have an empty default destructor, but won't always have a default constructor. For instance, all objects in the class must be default constructable in order for your class to even be considered to be default constructable. Also, if you have any custom constructors, the compiler will not define a default constructor.

Code: Select all

class B{ ... };
class A
{
public:
     // Having this prevents the compiler from creating a default constructor
     A( std::unique_ptr<B> b_ ) : b( std::move( b_ ) ){}

private:
     std::unique_ptr<B> b;
};
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Container : when do I need a move constructor ?

Post by albinopapa » March 31st, 2020, 6:01 pm

In class initialization is fine and doesn't need constructors, I suppose it's like the compiler writing a constructor for it.

Code: Select all

struct Vec2{ float x = 2.f, y = -2.f; };
Here no constructors, destructors nor assignment operators need be present and the x, y members will be initialized. It's like initializing an array:

int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
It copies the values into the memory space setup for the class.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply