patterncppMinor
Exercising in copy constructors and assignment operators
Viewed 0 times
constructorsexercisingassignmentandoperatorscopy
Problem
I've just created an implementation of array to train myself in copy constructors, assignment operators, and exception safety. Please look at this code and tell me what is wrong here.
class DumbArray {
public:
DumbArray(size_t sz)
: _size(sz), _buf(new int[_size])
{
memset(_buf, 0, _size * sizeof(int));
}
DumbArray(const DumbArray& arr)
: DumbArray(arr._size)
{
int* newMem = new int[arr._size];
if(newMem) {
std::copy(arr._buf, arr._buf + arr._size, newMem);
delete [] _buf;
_buf = newMem;
_size = arr._size;
}
}
DumbArray(DumbArray&& arr) {
swap(arr);
}
DumbArray(std::initializer_list ilist)
: DumbArray(ilist.size())
{
std::copy(ilist.begin(), ilist.end(), _buf);
}
DumbArray& operator=(const DumbArray& arr) {
if(this != &arr) {
DumbArray tmp(arr);
swap(tmp);
}
return *this;
}
DumbArray& operator=(DumbArray&& arr) {
swap(arr);
return *this;
}
~DumbArray() {
// _size = 0; // should this be done? object is being destroyed anyway, yup?
delete [] _buf;
}
void swap(DumbArray& arr) {
std::swap(_buf, arr._buf);
std::swap(_size, arr._size);
}
int& operator[](int idx) {
if(idx = _size) {
throw std::out_of_range("Incorrect index");
}
return _buf[idx];
}
size_t size() const {
return _size;
}
private:
size_t _size;
int* _buf;
};Solution
This is reasonably clean and reasonably complete code but I see some things that might improve your code.
Consider making this a template
There is no reason that this data structure should be limited to just storing
Implement member functions non-inline
All of those inline functions can increase the size of your code. If you declare them first and then have out-of-line implementations(at least for the non-trivial functions), it has advantages, especially if you decide to make this a templated class.
Consider making the destructor virtual
By having a concrete destructor rather than a virtual one, you are effectively prohibiting derived classes. If that's not your intent, making your destructor virtual will make it possible to derive classes from this one.
List the required
By my reckoning, this code requires the following to work:
These are therefore an essential part of the class interface.
Reconsider the use of
Although it's used often in the C standard library, you don't really need it here, and requiring a C-flavored include file doesn't help much in keeping the interface small. You could use
Avoid comparing signed to unsigned
In your
Add a default constructor
Because
cannot compile or run. You could either add a default value for your existing constructor or create a new default constructor.
Consider using
Instead of using the old-school
you might consider instead using
since you're already using the similar
Add a
As it is currently written, there's no way to index into a
Consider making this a template
There is no reason that this data structure should be limited to just storing
int values. Consider reworking it as a templated class to make it more general and to get additional practice.Implement member functions non-inline
All of those inline functions can increase the size of your code. If you declare them first and then have out-of-line implementations(at least for the non-trivial functions), it has advantages, especially if you decide to make this a templated class.
Consider making the destructor virtual
By having a concrete destructor rather than a virtual one, you are effectively prohibiting derived classes. If that's not your intent, making your destructor virtual will make it possible to derive classes from this one.
List the required
#include filesBy my reckoning, this code requires the following to work:
#include
#include
#include
#include These are therefore an essential part of the class interface.
Reconsider the use of
size_tAlthough it's used often in the C standard library, you don't really need it here, and requiring a C-flavored include file doesn't help much in keeping the interface small. You could use
unsigned instead and reduce the number of required include files.Avoid comparing signed to unsigned
In your
operator[], idx is declared as int but _size is size_t so the range check at the top of the function compares signed and unsigned numbers without a cast. Since negative numbers are already eliminated by the first clause, so you could use either a C-style cast (gasp!) or a reinterpret_cast on idx in the second clause. Add a default constructor
Because
DumbArray has no default constructor (one with no arguments), code like this:std::vector dv(10); // make ten arrayscannot compile or run. You could either add a default value for your existing constructor or create a new default constructor.
Consider using
std::fill_n instead of memsetInstead of using the old-school
memset(_buf, 0, _size * sizeof(int));you might consider instead using
fill_n: std::fill_n(_buf, _size, 0);since you're already using the similar
std::copy in one of the constructors.Add a
const version of operator[]As it is currently written, there's no way to index into a
const DumbArray. Adding const to operator[] will fix that problem but make sure it returns a copy of an int rather than a reference to one.Code Snippets
#include <cstring>
#include <initializer_list>
#include <iterator>
#include <stdexcept>std::vector<DumbArray> dv(10); // make ten arraysmemset(_buf, 0, _size * sizeof(int));std::fill_n(_buf, _size, 0);Context
StackExchange Code Review Q#52505, answer score: 5
Revisions (0)
No revisions yet.