assignment using overloaded constructor [duplicate] - c++

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 8 years ago.
I have created a custom vector class that uses a dynamic array to store data. The overloaded constructor takes a pointer to existing array and size of the array as the arguments.
int a[3] = { 1, 2, 3 };
Vector<int> v(a, 3);
However, when I try to change this vector using the following code, it crashes because the pointer of vector object "v" points to 0xcccccccc insted of the address of the dynamic array
v = Vector<int>(a, 3);
Why does this happen and how could I improve the assignment above?
EDIT: here is the calss code:
template <class T> class Vector
{
private:
T* mArray;
int Length;
public:
Vector(){
mArray = 0;
Length = 0;
};
Vector(const Vector& rVectorData){
Length = rVectorData.Length;
T* pArray = new T[Length];
for (int i = 0; i < Length; i++)
pArray[i] = rVectorData.mArray[i];
delete[] Array;
mArray = pArray;
};
Vector(const T* aArray, int size){
Length = size;
T* pArray = new T[Length];
for (int i = 0; i < Length; i++)
pArray[i] = aArray[i];
delete[] mArray;
mArray = pArray;
};
~Vector(){
delete[] mArray;
mArray = 0;
Length = 0;
};
}

delete[] mArray;
mArray = pArray;
Since this is occurring in your constructor, and you have not initialized mArray to anything (e.g. nullptr), you are attempting to delete some random area of memory (that you did not allocate), which will likely cause your program to crash (it is UB).
You can fix that by removing the delete[] mArray line as the constructor will only be called during construction.
"v" points to 0xcccccccc insted of the address of the array "a"
Since you are allocating memory in the constructor, v will not point to the address of a, as you are copying the values from a into v, which has allocated its own memory.
Additionally, since you did not define a copy-constructor, anytime you attempt to copy your vector, it will do a shallow copy. This will result in a memory corruption problem as whichever variable goes out of scope first will free the memory, leaving a dangling pointer in the other. When the latter finally goes out of scope, it will also result in UB (and likely crash your program).
As Karoly noted, when you implement your own destructor, you should follow the rule of 3.

It seems to me that if you're going to write your own vector class (or your own semi-duplicate of almost anything that's already widely available) you should try to not only make it correct, but add something new to the mix so your code isn't just a mediocre imitation of what's already easily available (along with preferably avoiding it's being a huge step backwards in any direction either).
For example, if I were going to support initializing a Vector from an array, I'd add a template member function that deduced the size of the array automatically:
template <size_t N>
Vector(T (&array)[N]) : data(new T[N]), size(N) {
std::copy_n(array, N, data);
}
This allows something like:
int a[]={1, 2, 3};
Vector<int> x(a);
...so you don't have to specify the size.
You've already heard about the rule of three. To avoid a step back from std::vector, you almost certainly want to update that to the rule of 5 (or else use a smarter pointer class that lets you follow the rule of zero).
The straightforward way to do that is to implement a move ctor and move assignment operator:
Vector &operator=(Vector &&src) {
delete[] data;
data=src.data;
size=src.size;
src.data=nullptr;
src.size = 0;
return *this;
}
Vector(Vector &&src): data(src.data), size(src.size) {
src.data=nullptr;
src.size=0;
}
For convenience, you also almost certainly also want to include a ctor that takes an initializer list:
Vector(std::initializer_list<T> const &i) : data(new T[i.size()]), size(i.size())
{
std::copy(i.begin(), i.end(), data);
}
Finally, you just about need (or at least really want) to support an iterator interface to the contained data:
class iterator {
T *pos;
friend class Vector;
iterator(T *init): pos(init) {}
public:
iterator &operator++() { ++pos; return *this; }
iterator &operator--() { --pos; return *this; }
iterator &operator++(int) { iterator tmp(*this); ++pos; return tmp; }
iterator &operator--(int) { iterator tmp(*this); --pos; return tmp; }
T &operator*() { return *pos; }
bool operator!=(iterator const &other) const { return pos!=other.pos; }
};
iterator begin() { return iterator(data); }
iterator end() { return iterator(data+size); }
...then you want to add const_iterator, reverse_iterator and const_reverse_iterator classes, and with them cbegin/cend, rbegin/rend and crbegin/crend to support constant and/or reversed iteration of the data.
Note, however, that most of this is just duplicating what std::vector already provides. The only new thing we've added here is the ctor that takes an array and deduces its size automatically. At the same time, that is enough to provide a fixed-size array wrapper that (other than dynamic sizing) has approximate parity with std::vector.

Related

Should I check and free pointer in VLA class' assign operator (operator=)

So let's say we have a simple variable-length array class like following:
struct VLA {
double* d=nullptr;
int dim;
}
What makes me wonder is, within operator=, should I check (and perhaps free/delete if not nullptr) d before malloc/new an new array for it? As assignment is different from copy constructor where it might originally also carried an array.
Like following example:
operator=(VLA &other) {
double *tmp=new double[dim];
memcpy(tmp, other.d, sizeof(double)*other.dim);
delete[]d;
d=tmp;
dim=other.dim;
}
Is the delete[]d required?
within operator=, should I check (and perhaps free/delete if not nullptr) d before malloc/new an new array for it?
Is the delete[]d required?
If you are going to allocate a new array with new[], then yes, you need to free the the old array with delete[], otherwise it will be leaked. Whether you do that before or after allocating the new array is up to you, but I would do it after, in case allocating the new array fails.
Note that you can optimize the code you have shown a little, by skipping the new[]/delete[] if the new dim is the same value as the old dim, and by using the copy-swap idiom when you do allocate a new array:
VLA(const VLA &other) {
d = new double[other.dim];
memcpy(d, other.d, sizeof(double) * other.dim);
dim = other.dim;
}
VLA& operator=(const VLA &other) {
if (this != &other) {
if (dim == other.dim) {
memcpy(d, other.d, sizeof(double) * other.dim);
else {
VLA temp(other);
std::swap(d, temp.d);
std::swap(dim, temp.dim);
}
}
return *this;
}
Though, you really should should be using std::vector instead of new[]/delete[] manually at all. Let std::vector handle the array for you:
#include <vector>
struct VLA {
std::vector<double> d;
int dim() const { return d.size(); }
// compiler-generated copy constructor will "do the right thing"...
// compiler-generated operator=() will "do the right thing"...
};

How to free memory with move constructor

I am trying to create my own vector and here is a minimal example to introduce the problem I have:
class DemoVector {
public:
DemoVector() : capacity_(1), size_(0) {
data_ = new int[1];
}
DemoVector(DemoVector&& rhs) {
data_ = std::move(rhs.data_);
size_ = rhs.size_;
capacity_ = rhs.capacity_;
}
~DemoVector() {
delete[] data_;
}
void PushBack(const int &v) {
// doesn't matter
}
private:
int *data_;
size_t capacity_;
size_t size_;
};
Test:
TEST_CASE("Test") {
DemoVector b;
b.PushBack(1);
DemoVector c(std::move(b));
}
I have a problem here and I understand why. I have two objects which points on the same memory. Second destructor tries to free memory, which have already been freed by first destructor.
But I don't know how to fix it.
Thank you for your help.
std::move(rhs.data_) doesn't actually move anything. std::move is nothing more than a named cast. It produces an rvalue reference that allows move semantics to occur. But for primitive types, it's just a copy operation. The pointer is being copied, and so you end up with two pointers that contain the same address. Since you don't want the source object to still be pointing at the the same buffer, simply modify it. That's why move-semantics is build around non-const references.
Move constructors are commonplace now, so there's a standard utility (C++14) to help write them in a way that makes code behave more as you'd expect. It's std::exchange. You can simply write
DemoVector(DemoVector&& rhs)
: data_(std::exchange(rhs.data_, nullptr))
, size_(std::exchange(rhs.size_ , 0))
, capacity_(std::exchange(rhs.capacity_ , 0))
{}
And all the values get adjusted properly. std::exchange modifies its first argument to hold the value of the second argument. And finally, it return the old value of the first argument. Very handy to shift values around in one-liner initializations.
Because std::move is basically just a cast that doesn't actually move anything! You need to update the values in the other object yourself:
DemoVector(DemoVector&& rhs) {
data_ = rhs.data_;
size_ = rhs.size_;
capacity_ = rhs.capacity_;
rhs.data_ = nullptr;
rhs.size_ = 0;
rhs.capacity = 0;
}
Or alternatively to make use of the existing constructor:
DemoVector(DemoVector&& rhs): DemoVector() {
// Or write your own swap function to reuse this elsewhere
std::swap(data_, rhs.data_);
std::swap(size_, rhs.size_);
std::swap(capacity_, rhs.capacity_);
}
It's up to you how you want users of your class to handle moved-from objects. In the second case, and possibly also the first depending on how the rest of your class works, rhs (b in your test case) will be an empty vector.

How does move semantics apply on the following snippet when no xvalue is present?

I stumbled upon the following article and do not understand the performance difference between C++98 and C++11 that is, as the author says, attributed to move semantics.
#include <vector>
using namespace std;
int main() {
vector<vector<int> > V;
for(int k = 0; k < 100000; ++k) {
vector<int> x(1000);
V.push_back(x);
}
return 0;
}
To the best of my knowledge, V.push_back(x) does not invoke any move semantics. I believe that the x is an lvalue and this snippet is invoking the same vector::push_back(const T&) in both C++98 and C++11.
The code compiles identically on either version: https://godbolt.org/z/q3Lzae
Is the author incorrect with his statement, or is the compiler smart enough to realize x is about to be destroyed?
If the author is incorrect, is there anything else present in C++11 that would have given this the performance boost "without changing a line of code"?
You're correct that object x won't be moved from. The move operations gaining performance have to do with the other k vectors already in V.
As a vector grows (unless reserve was used with a sufficient size), it will sometimes need to reallocate to get a bigger chunk of memory, since its elements are required to be in contiguous memory. This doesn't happen on every push_back, but it will certainly happen sometimes in this example. So let's say push_back and other functions make use of some private function grow_capacity, which gets enough memory, and then creates objects already in the vector within that memory.
In C++03, the only reasonable way to create the objects in the new memory, for an arbitrary template parameter T, is using the copy constructor of T.
// C++03 implementation?
template <typename T, typename Alloc>
std::vector<T, Alloc>::grow_capacity(::std::size_t new_capacity)
{
T* new_data = get_allocator().allocate(new_capacity);
T* new_end = new_data;
try {
for (const_iterator iter = begin(); iter != end(); ++iter) {
::new(static_cast<void*>(new_end)) T(*iter); // T copy ctor!
++new_end;
}
} catch (...) {
while (new_end != new_data) (--new_end)->~T();
get_allocator().deallocate(new_data, new_capacity);
throw;
}
// Clean up old objects and memory.
for (const_reverse_iterator riter = rbegin(); riter != rend(); ++riter)
riter->~T();
get_allocator().deallocate(_data, _capacity);
// Assign private members.
_data = new_data;
_capacity = new_capacity;
}
In C++11 and later, when std::vector<T> needs to reallocate to a larger capacity, it is allowed to move its T elements instead of copying them if it can do so without breaking the strong exception guarantee. This requires that the move constructor is declared to not throw any exceptions. But if the move constructor might throw, the elements need to be copied in the old way, to make sure the vector will remain in a consistent state if that happens.
// C++17 implementation?
template <typename T, typename Alloc>
std::vector<T, Alloc>::grow_capacity(::std::size_t new_capacity)
{
T* new_data = get_allocator().allocate(new_capacity);
if constexpr (::std::is_nothrow_move_constructible_v<T>) {
::std::uninitialized_move(begin(), end(), new_data); // T move ctor!
} else {
T* new_end = new_data;
try {
for (const T& old_obj : *this) {
::new(static_cast<void*>(new_end)) T(old_obj); // T copy ctor!
++new_end;
}
} catch (...) {
while (new_end != new_data) (--new_end)->~T();
get_allocator().deallocate(new_data, new_capacity);
throw;
}
}
for (const_reverse_iterator riter = rbegin(); riter != rend(); ++riter)
riter->~T();
get_allocator().deallocate(_data, _capacity);
// Assign private members.
_data = new_data;
_capacity = new_capacity;
}
So in the container with type std::vector<std::vector<int> >, T is std::vector<int>. Growing the capacity the C++03 way will sometimes require a large number of copy constructors then destructors for the std::vector<int>. Each copy constructor allocates some memory and copies 1000 int values, and each destructor deallocates some memory, so this will really add up. But with the C++11 std::vector, since the element type std::vector<int> does have a noexcept move constructor, the std::vector<std::vector<int>> container can just use that move constructor, which is just a few swaps of scalar members and also causes the destructors of the moved-from old objects to do nothing.
What's going on in this example is that x is about to go out of scope in the push_back call (its the end of its lifetime, and there is no subsequent use), so the compiler may treat it as an xvalue and move out of it. Its not one of the cases where the compiler is required to do a move optimization, so it might not, but any decent compiler will if optimization is enabled (both gcc and clang will use a move here).

operator overloading memory leak

Recently I got a task to do in C++, implement a Set class with union, intersection etc. as overloaded operators. I've got a problem with overloading an operator+(). I decide to use vectors and get the advantage of some algorithm's library functions. The problem is I HAD TO pass to constructor an array pointer and array size. This complicated this task a bit... I can compile it but during the "z=a+b" operation I encounter somekind of memory leak. Can anyone explain me what am I doing wrong?
class Set {
int number; // array size (can't be changed)
int *elems; // array pointer (same)
public:
Set();
Set(int, int*); // (can't be changed)
~Set();
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
};
Set::Set(){};
Set::Set(int n, int* array){
number=n;
elems = array = new int[number];
for(int i=0; i<number; i++) // creating Set
std::cin >> elems[i];
std::sort(elems, elems + number);
}
Set::~Set(){
delete[] elems;
}
int main(){
int* pointer;
Set z;
Set a = Set(5, pointer);
Set b = Set(2, pointer);
z=a+b;
}
I added copy constructor and copy assingment, changed the operator+() as NathanOliver advised and now I am passing to constructor static array. Still have memory leak and strange thing is that I got this memory leak even when in main there is only class variable initialization, doesn't matter if with parameters or not... Any suggestions? I think cunstructor is valid.
Set::Set(int n, int* array){
number = n;
elems = array;
std::sort(elems, elems + number);
}
Set::Set(const Set& s){
number=s.number;
elems=s.elems;
}
Set& operator=(const Set& X){
if(this==&X)
return *this;
delete [] elems;
elems=X.elems;
number=X.number;
return *this;
I use gcc (tdm64-2) 4.8.1 compiler.
In
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
You create a vector, modify it and then set the elems to point to what the vector contains. The issue with that is that when the vector is destroyed at the end of the function the memory that the vector held is released. So you now have a pointer pointing to memory you no longer own. Trying to do anything with it is undefined behavior. What you could do is create a new array, copy the elements of the vector into the array and then assign the new array to `elems
Set Z;
Z.number= v.size();
Z.elems= new int[z.number];
for (int i = 0; i < Z.number; i++)
Z.elems[i] = v[i];
return Z;
Secondly you need to define a copy constructor and assignment operator for you class. To do that reference: What is The Rule of Three?
The best solution (but I can't tell if you're allowed to do this specifically) is to use vector internally in your Set and assign it from the passed in pointer and length using the two-iterator constructor.
Now if that's not possible you need to properly manage the memory of your class:
You need a copy constructor and copy assignment operator.
In your operator+ you can't create a local vector and then take the address of its memory, that memory will disappear as soon as the operator returns.
Possibly other things I didn't catch.
When you have z=a+b, the assignment operator for the Set class is used. You didn't define a custom version of this operator, so the default compiler-generated one is used. This compiler-generated assignment operator=() simply does a member-wise copy.
Since you have raw owning pointers in your Set class, this doesn't work correctly: the default compiler-generated operator=() shallow-copies pointers, instead you should deep-copy the data.
An option to fix this is to define your own version of operator=(), paying attention to do proper deep-copy of source data.
Note that in this case you also should define a copy constructor.
But a better option would be to get rid of the owning raw pointer data members, and instead use a RAII building block class, like std::vector.
So, for example, instead of these data members:
int number; // array size (can't be changed)
int *elems; // array pointer (same)
you could just have a single:
std::vector<int> elems;
If you do that, the default compiler-generated operator=() will work just fine, since it will copy the std::vector data member (not the raw owning pointers), and std::vector knows how to properly copy its content without leaking resources.

What std::vector in C++ really is? [duplicate]

This question already has answers here:
How is vector implemented in C++
(9 answers)
Closed 8 years ago.
Not so far I discovered, that I completely have no clue about nature of std::vector.
Let me explain:
Vector is growable, right? That means, inside it must somehow allocate/reallocate memory dynamically. Something like this:
class vector {
private:
int *data;
};
Okay. But such definition implies the fact that if we pass std::vector to another function by reference or by value -- there will be no difference between this two types of parameter passing, and both function will be able to modify data (unless vector is passed as const).
BUT! I tried the following and my idea failed:
void try_to_modify(vector<int> v) {
v[2] = 53;
}
int main() {
vector<int> v(3);
v[2] = 142;
try_to_modify(v);
cout << v[2] << '\n'; // output is: 142
return 0;
}
So where's the truth? What std::vector really is?
Thank you.
std::vector is a container, which internally manages its memory and provides a custom copy constructor. In this copy constructor, new memory is allocated and the existing data is copied over, which makes it a expensive operation. If you want to pass a vector without copying the contained data, you can pass by const reference, for example, const std::vector<int>&.
Let's take a look how to implement a basic container like std::vector.
template <typename T>
class MyVector
{
public:
MyVector (int size)
: data_ (new T[size])
, size_ (size)
{}
~MyVector ()
{
delete [] data_;
}
private:
T* data_ = nullptr;
int size_ = 0;
};
If we copy such an object, we'll have two problems. First, as you noticed, the memory will point to the same location. Second, we will have two destructors that will destruct the same memory, resulting in a double-free. So, let's add a copy constructor, which will be invoked whenever a copy is made.
MyVector (const MyVector& other)
: size_ (other.size_)
{
data_ = new T[size_];
std::copy (other.data_, other.data_ + size_, data_);
}
MyVector& operator= (const MyVector& other)
{
// allocate and copy here to allow for self-assignment
auto newData = new T[other.size_];
std::copy (other.data_, other.data_ + size_, newData);
delete [] data_;
size_ = other.size_;
data_ = newData;
return *this;
}
And that's how std::vector works internally.
This isn't so much a problem with vector as it is with pass-by-value and pass-by-reference arguments.
void try_to_modify (vector<int> vec) will send a copy of the original vector to the function and the function operates on a copy. The vector will copy the data when a copy is made. i.e. new pointer to new data.
However, if you define your function as: void try_to_modify(vector<int> & vec) then it will send the exact vector to the function and your function will operate on it.
Passing by reference is much faster for objects and is usually preferable, unless you have a specific need for a copy.