Copy, move, and assignment operators with a derived class

Started by
22 comments, last by Bregma 6 years, 6 months ago

When it comes to the copy, move, and assignment operators of a child class how is it supposed to look?

If this is the implementation of my parent class


//Default constructor
Buffer::Buffer()
{
	buffer = nullptr;
}

//Copy constructor
Buffer::Buffer(const Buffer& other)
{
	buffer = other.buffer;
	buffer->AddRef();
}

//Move constructor
Buffer::Buffer(Buffer&& other)
{
	buffer = other.buffer;
	buffer->AddRef();
	other.release(); //Free the buffer resource of the other instance
}

//Destructor
Buffer::~Buffer()
{
	release();
}

//Copy assignment
Buffer& Buffer::operator=(const Buffer& other)
{
	if (&other != this)
	{
		release(); //Free the buffer of this instance

		buffer = other.buffer;
		buffer->AddRef();
	}

	return *this;
}

//Move assignment
Buffer& Buffer::operator=(Buffer&& other)
{
	if (&other != this)
	{
		release(); //Free the buffer of this instance

		buffer = other.buffer;
		buffer->AddRef();

		other.release(); //Free the buffer of the other instance
	}

	return *this;
}

And this is the implementation of my child class. Is this correct?

I'm really just wondering about the at the copy, move, and I'm not too sure about the assignment operators


//Default constructor
VertexBuffer::VertexBuffer() : Buffer()
{
}

//Copt constructor
VertexBuffer::VertexBuffer(const VertexBuffer& other) : Buffer(other)
{
}

//Move constructor
VertexBuffer::VertexBuffer(VertexBuffer&& other) : Buffer(other)
{
}

//Destructor
VertexBuffer::~VertexBuffer()
{
}

//Not sure how to handle the copy/move operators.
//Do I treat them as there own operators and copy the Buffer assignment operators code?
VertexBuffer& VertexBuffer::operator=(const VertexBuffer& other)
{
}

VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other)
{
}

 

Advertisement

Deriving from a copyable base class is a bit of a code smell IMHO (but so is deriving from concrete classes in general :) ). FWIW I don't know if I've ever needed to solve this problem... 

As for how to call the base class assignment operator, you can always do something like:

*static_cast<Base*>(this) = other 

8 minutes ago, Hodgman said:

Deriving from a copyable base class is a bit of a code smell IMHO (but so is deriving from concrete classes in general ). FWIW I don't know if I've ever needed to solve this problem... 

As for how to call the base class assignment operator, you can always do something like:

*static_cast<Base*>(this) = other 

Just make it very explicit: Base::operator=(other);

🧙

A copy assignment operator should always look like this.


VertexBuffer& VertexBuffer::operator=(const VertexBuffer other)
{
	this->swp(other);
	return *this;
}

Of course, you need to implement your swap() member function, but that should be fairly trivial.  See, the magic of this is it implements the copy assignment operator in terms of the copy constructor, which is generally the expected behaviour, so you only write the meaty function once.  Even your base class should implement the operator this way.  Trace it through, you'll see what I mean.

Also, why would you create a class hierarchy without a virtual destructor in the base class?  That smells of a misuse of inheritance.

Stephen M. Webb
Professional Free Software Developer

16 minutes ago, Bregma said:

A copy assignment operator should always look like this.



VertexBuffer& VertexBuffer::operator=(const VertexBuffer other)
{
	this->swp(other);
	return *this;
}

Of course, you need to implement your swap() member function, but that should be fairly trivial.  See, the magic of this is it implements the copy assignment operator in terms of the copy constructor, which is generally the expected behaviour, so you only write the meaty function once.  Even your base class should implement the operator this way.  Trace it through, you'll see what I mean.

Also, why would you create a class hierarchy without a virtual destructor in the base class?  That smells of a misuse of inheritance.

Shoudn't the argument be a reference for a general copy assignment operator?

My understanding of the default copy assignment operator was to call the base copy assignment operator first and then the copy assignment operators of each of the object's members.

🧙

Why are you using inheritance here? Do you have anywhere in your code a pointer or reference to a buffer that might be a vertex buffer or a different type of buffer, and you'll only know at run time? That would be about the only situation in which I'd use inheritance.

Inheritance is easily abused. Don't use it until you really need it.

 

7 hours ago, Hodgman said:

Deriving from a copyable base class is a bit of a code smell IMHO (but so is deriving from concrete classes in general )

I actually don't mean for the Buffer class to be concrete I just don't know how to make it not since there are no methods that need to be implemented for the child


//Parent class
class Buffer
{
public:
	Buffer();
	Buffer(const Buffer& other);
	Buffer(Buffer&& other);
	virtual ~Buffer();
	Buffer& operator=(const Buffer& other);
	Buffer& operator=(Buffer&& other);

private:
	ID3D11Buffer *buffer;
	void release(); //Releases the buffer member variable and cleans it up
};


//VertexBuffer child class, other versions would be ConstantBuffer or IndexBuffer
class VertexBuffer : public Buffer
{
public:
	VertexBuffer();
	VertexBuffer(const VertexBuffer& other);
	VertexBuffer(VertexBuffer&& other);
	~VertexBuffer();
	VertexBuffer& operator=(const VertexBuffer& other);
	VertexBuffer& operator=(VertexBuffer&& other);
};

 

7 hours ago, matt77hias said:

Just make it very explicit: Base::operator=(other);

Do I need to set anything to the left hand side when I do this? or is it really just:


//Vertex Buffer copy assignment opt
VertexBuffer& VertexBuffer::operator=(const VertexBuffer& other)
{
	if (&other != this)
	{
		Buffer::operator=(other);
	}

	return *this;
}

//Vertex Buffer move assignment opt
VertexBuffer& VertexBuffer::operator=(VertexBuffer&& other)
{
	if (&other != this)
	{
		Buffer::operator=(other);
	}
  
	return *this;
}

 

1 hour ago, Bregma said:

why would you create a class hierarchy without a virtual destructor in the base class?

I posted the implementation file contents before. Intellisense gets mad at me if use the virtual keyword there, but its in the header file :)

 

15 minutes ago, alvaro said:

Why are you using inheritance here? Do you have anywhere in your code a pointer or reference to a buffer that might be a vertex buffer or a different type of buffer, and you'll only know at run time? That would be about the only situation in which I'd use inheritance.

Inheritance is easily abused. Don't use it until you really need it.

Currently I really don't have a point where I need a container of Buffers. For now I will definitely know that I need a VertexBuffer here or I should use a IndexBuffer there. I guess I really just have a base class, because regardless of whether my instance object is a VertexBuffer, ConstantBuffer, or XXXXBuffer they are still all buffers at the core and all have that ID3D11Buffer *buffer member variable in common

12 minutes ago, noodleBowl said:

I actually don't mean for the Buffer class to be concrete I just don't know how to make it not since there are no methods that need to be implemented for the child

If the derived class destructor needs to be called as well, when the object is destroyed via the base class destructor, you need a virtual destructor in the base class.

12 minutes ago, noodleBowl said:

Do I need to set anything to the left hand side when I do this? or is it really just:

That is ok if you have no data to copy for the VertexBuffer itself. For the move assignment operator, you need to use std::move(other), otherwise you will invoke the copy instead of move assignment operator of the base class.

If this is literally the implementation you want, just use = default for both.

🧙

13 minutes ago, noodleBowl said:

I posted the implementation file contents before. Intellisense gets mad at me if use the virtual keyword there, but its in the header file :)

I don't know why Intellisense gets mad, but normally you shouldn't care about Intellisense. The only one who really knows and is allowed to complain is your compiler (output window).

🧙

13 minutes ago, noodleBowl said:

Currently I really don't have a point where I need a container of Buffers. For now I will definitely know that I need a VertexBuffer here or I should use a IndexBuffer there. I guess I really just have a base class, because regardless of whether my instance object is a VertexBuffer, ConstantBuffer, or XXXXBuffer they are still all buffers at the core and all have that ID3D11Buffer *buffer member variable in common

 

You don't need to inherit to share functionality. Composition will be better in the vast majority of situations. So both VertexBuffer and ConstantBuffer can have a member of type Buffer (or MemoryBuffer, or RawBuffer... Using good names is important) and share functionality that way.

 

This topic is closed to new replies.

Advertisement