So I have to write an operator= method in c++ that copies all the values of one array into another. Here's what I wrote:
dynamic_array &dynamic_array::operator=(const dynamic_array &a) {
size = a.get_size();
if (size % BLOCK_SIZE == 0){ //a multiple of BLOCK_SIZE
allocated_size = size;
} else {
int full_blocks = size / BLOCK_SIZE;
allocated_size = (full_blocks+1) * BLOCK_SIZE;
}
try {
array = new int[allocated_size];
} catch (bad_alloc){
throw exception (MEMORY_EXCEPTION);
}
//copy a[i..size-1]
for (int i = 0; i < size; i++){
array[i] = a[i];
}
return *this; //returns a reference to the object
}
So it doesn't assume anything about the sizes and sets the size and allocated size of the array it's given (and using another get_size() method). Now the second code I have to write just says I have to create an array containing a copy of the elements in a. Now I just wrote the same thing as I did for my operator= method (without returning anything):
dynamic_array::dynamic_array(dynamic_array &a) {
size = a.get_size();
if (size % BLOCK_SIZE == 0){ //a multiple of BLOCK_SIZE
allocated_size = size;
} else {
int full_blocks = size / BLOCK_SIZE;
allocated_size = (full_blocks+1) * BLOCK_SIZE;
}
try {
array = new int[allocated_size];
} catch (bad_alloc){
throw exception (MEMORY_EXCEPTION);
}
//copy a[i..size-1]
for (int i = 0; i < size; i++){
array[i] = a[i];
}
}
Now this is giving me the output that I want but I'm just wondering if there's an easier way to do these two methods. The methods are for a dynamic array and I feel like there's more lines of code than needed. The operator= one is supposed to copy the elements of a into a new dynamic array and dynamic_array::dynamic_array(dynamic_array &a) { is supposed to create a new array containing a copy of the elements in a. It sounds like the same code for each method because you always need to create a new array and you will always need to copy the array elements from one array to another but is there a more simpler way to write these two methods or is this the simplest way to do it?
You could simplify both the copy constructor and the copy assignment operator by changing the type of array to std::vector<int>.
The vector does all the work for you and you don't even need any custom implementations at all.
First to make this simpler, you can make use of the fact that you have access to all private members of a. Also the use of memcpy will make this perform better. A couple of lines are added as well to delete the existing array before copying.
dynamic_array &dynamic_array::operator=(const dynamic_array &a) {
size = a.size;
allocated_size = a.allocated_size;
int* newArray = new int[allocated_size];
//copy array
memcpy(newArray, a.array, allocated_size * sizeof(int));
// Delete existing array
if (array != NULL)
delete[] array;
array = newArray;
return *this; //returns a reference to the object
}
Secondly, for copy constructor, a trick we used to do is to call the equal operator inside the copy constructor in order not to repeat the code:
dynamic_array::dynamic_array(dynamic_array &a) : array(NULL) {
*this = a;
}
From Scot Meyers (classic) book "Effective C++":
"In practice, the two copying functions will often have similar bodies, and this may tempt you to try to avoid code duplication by having one function call the other. Your desire to avoid code duplication is laudable, but having one copying function call the other is the wrong way to achieve it.
It makes no sense to have the copy assignment operator call the copy constructor, because you'd be trying to construct an object that already exists. This is so nonsensical, there's not even a syntax for it. There are syntaxes that look like you're doing it, but you're not; and there are syntaxes that do do it in a backwards kind of way, but they corrupt your object under some conditions. So I'm not going to show you any of those syntaxes. Simply accept that having the copy assignment operator call the copy constructor is something you don't want to do.
Trying things the other way around — having the copy constructor call the copy assignment operator — is equally nonsensical. A constructor initializes new objects, but an assignment operator applies only to objects that have already been initialized. Performing an assignment on an object under construction would mean doing something to a not-yet-initialized object that makes sense only for an initialized object. Nonsense! Don't try it.
Instead, if you find that your copy constructor and copy assignment operator have similar code bodies, eliminate the duplication by creating a third member function that both call. Such a function is typically private and is often named init. This strategy is a safe, proven way to eliminate code duplication in copy constructors and copy assignment operators."
And by the way, it'd be wise to also check for self-equality in you assignment operator.
Related
So, for instance, I have the following code which I want a object's pointer
member to point to a memory which was pointed by another temporary object's
member.
struct A {
int * vals;
A(): vals(new int[10]) { }
~A(){ delete[] vals; }
};
int main() {
A a;
{
A temp;
for (int i = 0; i < 10; ++i) {
temp.vals[i] = 100;
}
a.vals = temp.vals;
temp.vals = nullptr; // avoid double free
}
I set temp.vals to nullptr in case the destructor of temp will free that
memory. So far so good, I guess. However, if I change the vals to a dynamic
array, i.e. a pointer to pointers:
struct A {
int ** vals;
A(): vals(new int*[10]) {
for (int i = 0; i < 10; ++i) {
vals[i] = new int;
}
}
~A(){
for (int i = 0; i < 10; ++i) {
delete vals[i]; // illegal to dereference nullptr
}
delete [] vals;
}
};
int main() {
A a;
{
A temp;
for (int i = 0; i < 10; ++i) {
temp.vals[i] = new int(1);
}
a.vals = temp.vals;
temp.vals = nullptr; // avoid double free
}
}
I have add a for loop in destructor to handle the nested allocated memory, and
to avoid the memory be freed by the destructor of temp, I set temp.vals to
nullptr, which, however will cause a segmentation fault since when destructor
of temp is called, it is illegal to dereference a nullptr.
So my question is, how to correct set the destructor to handle the dynamic array.
I'm not a native speaker, so please forgive my grammar mistakes.
The typical C++ solution looks a bit different:
class A {
private:
int* vals;
public:
A(): vals(new int[10]) { }
~A(){ delete[] vals; }
A (A const& src); // Copy constructor
A (A&& src) : vals (src.vals) { src.vals = nullptr; }
A& operator=(A const&); // Assignment
A& operator=(A &&);
};
You can now write a = std::move(temp). Outside the class, you don't need to know how the inside works.
For your 2D array, just define the same special member functions. This is usually called the "Rule of Five". If you need a destructor, you probably need the other 4 functions as well. The alternative is the "Rule of Zero". Use std::vector or another class that manages memory for you.
However, if I change the vals to a dynamic array, i.e. a pointer to pointers
In the first program, vals is a pointer. It points to a dynamic array of integers. In the second program, vals is also a pointer. It points to a dynamic array of pointers.
how to correct set the destructor to handle the dynamic array.
You set vals to null. If null is a valid state for vals, then it isn't correct to unconditionally indirect through it in the destructor. You can use a conditional statement to do so only when vals isn't null.
However, the program is hardly safe because vals is public, and thus it is easy to mistakenly write a program where it is assigned to point to memory that isn't owned by the object. In cases where destructor cleans up an owned resource, it is important to encapsulate the resource using private access to prevent accidental violation of class invariants that are necessary to correctly handle the resource.
Now that vals is no longer outside of member functions, you cannot transfer the ownership like you did in your example. The correct way to transfer the ownership is to use move assignment (or constructor). The implicitly generated move constructor cannot handle an owning bare pointer correctly. You must implement them, as well as the copy assignment and constructor.
Furthermore, you should use an owning bare pointer in the first place. You should use a smart pointer instead. If you simply replaced the bare pointers with unique pointer, then the implicitly generated move assignment and constructor would handle the resource correctly, and the copy assignment and constructor would be implicitly deleted.
Lastly, the standard library has a container for dynamic arrays. Its called std::vector. There's typically no need to attempt to re-implement it. So in conclusion, I recommend following:
std::vector<int> a;
{
std::vector<int> temp;
for (int i = 0; i < 10; ++i) {
temp.vals[i] = 1;
}
a = std::move(temp);
}
There are still issues such as the temporary variable being entirely unnecessary, and the loop could be replaced with a standard algorithm, but I tried to keep it close to the original for the sake of comparison.
P.S. It's pretty much never useful to dynamically allocate individual integers.
I have a simple Box container with a naive implementation that takes Car
#include <iostream>
#include <vector>
struct Car {
Car() { puts("def"); }
Car(Car const& other) { puts("copy"); }
Car& operator=(Car const& other) {
puts("assign");
return *this;
}
};
struct Box {
size_t size;
Car* ptr;
Box(size_t size)
: size(size)
, ptr{new Car[size]}
{}
Box(Box const& other)
: size{other.size}
{
ptr = new Car[size];
for (int i = 0; i < size; i++)
ptr[i] = other.ptr[i]; // hits operator=
}
};
int main() {
Box b(2);
Box b3 = b;
std::cout << std::endl;
std::vector<Car> v(2);
std::vector<Car> v2 = v;
}
o/p
def
def
def
def
assign
assign
def
def
copy
copy
std::vector copies and calls the copy constructor and Box doesn't. How std::vector's copy constructor is implemented? and what I am doing wrong?
How std::vector's memory allocation is handled and why default constructor is called only two times in std::vector where as 4 times in Box? Any explanation would suffice
new[] combines allocating memory with starting the lifetime of the elements in the array. This can be problematic, as you've seen, because it calls the default constructor of each element.
What std::vector does is use std::allocator (or whatever allocator you provided as the second template argument) to allocate memory then uses placement new to start the lifetime of the array's elements one-by-one. Placement new is a new expression where the developer provides a pointer to where the object should be created, instead of asking new to allocate new storage.
Using this approach, here is a simplified example of your copy constructor :
Box::Box(const Box & other) : size{other.size}
{
// Create storage for an array of `size` instances of `Car`
ptr = std::allocator<Car>{}.allocate(size);
for(std::size_t i = 0; i < size; ++i)
{
// Create a `Car` at the address `ptr + i`
// using the constructor argument `other.ptr[i]`
new (ptr + i) Car (other.ptr[i]);
}
}
With this approach, you can't use delete[] or delete to clean up your Car elements. You need to explicitly perform the previous process in reverse. First, explicitly destroy all the Car objects by calling each of their destructors, then deallocate the storage using the allocator. A simplified destructor would look like :
Box::~Box()
{
for(std::size_t i = 0; i < size; ++i)
{
// Explicitly call the destructor of the `Car`
ptr[i].~Car();
}
// Free the storage that is now unused
std::allocator<Car>().deallocate(ptr, size);
}
The copy assignment operator will involve both of these processes, first to release the clean up the previous elements, then to copy the new elements.
Here is a very rudimentary implementation for Box : https://godbolt.org/z/9P3sshEKa
It is still missing move semantics, and any kind of exception guarantee. Consider what happens if new (ptr + i) Car (other.ptr[i]); throws an exception. You're on the line for cleaning up all the previously created instances, as well as the storage. For example, if it throws at i == 5 you need to call the destructors of the Car objects 0 through 4, then deallocate the storage.
Overall, std::vector does a lot of heavy lifting for you. It is hard to replicate its functionalities correctly.
std::vector uses an allocator instead of using the new operator. The crucial difference is that the new operator constructs every single element in the array. But vector allocates raw memory and only constructs elements on demand. You could achieve the same by using operator new (instead of new operator), malloc, some allocator, or by other means. You then use placement-new to call the constructors. In destructor, you have to call destructors of all elements individually and only then free the memory. See:
Difference between 'new operator' and 'operator new'?
malloc
std::allocator
operator new
new expression
Additionally, your Box class needs an operator= since the default one does something wrong. And a destructor. It's leaking memory.
Perhaps used the reference counting mechanism in the implementation of the lower layers of the allocation.
I have seen several examples of copy assignment operator and could not understand why do we need to delete pointers inside copy assignment operator. For example if I have the following class
class MyClass
{
public:
MyClass(int a)
{
x = new int(a);
}
MyClass& operator=(const MyClass& pMyClass)
{
*x = *(pMyClass.x);
// ?????????
// delete x;
// x = new int(*(pMyClass.x));
}
~MyClass()
{
delete x;
}
private:
int* x;
}
What is wrong with *x = *(pMyClass.x) line? I am just copying object pointed by pMyClass.x why I need to delete and create it again?. Could anyone please give example when this code will cause memory leak?
So this is an example [extracted from Bjarne Stroustrup's "A tour of C++ (2nd edition)] of a copy assignment of a user defined vector class:
Vector& Vector::operator=(const Vector& a) // copy assignment
{
double∗ p = new double[a.sz];
for (int i=0; i!=a.sz; ++i)
p[i] = a.elem[i];
delete[] elem; // delete old elements
elem = p; // here elem is the vector's data holding member array
sz = a.sz;
return ∗this;
}
To understand why at line 6 we have the deletion operation:
delete[] elem; // delete old elements
we first need to first understand the distinction between copy constructor and copy assignment. In the first case (copy constructor) we create a completely new object, whereas in the second case (copy assignment, the one we're actually interested in) we already have an existing object into which we just want to copy the contents of another given object of the same type.
Given the fact that we already have an existing object, we first need to clear it's contents so that we are then able to copy the desired content from the object we intent to copy.
I hope that answers your question.
It is a valid code. But if instead of the pointer to a single object you will have a pointer to first element of an array and arrays may have different sizes then you need to delete the array that to reallocate it with the new size.
Nothing wrong with *x = *(pMyClass.x) when you copying value from one class instance to other. I think, in general, deleting an object (if it is not just int) can prevent usage of new object with new data if before operator= execution address stored in x was sent to some other part of program.
I try to write a copy constructor for my vector of pointers to object initialized and declared in class Shop. The vector in consideration is:
std::vector <gCustomer*> vCustomer;
It has been also declared in the constructor of gShop and deleted via loop in the destructor.
Now I want to have a deep copy of the vector of pointers in the copy constructor. But nothing gets actually copied, a check of its size remains zero or crashes the program if I manage to run the program and access vCustomer. (Note if I leave the copy constructor out so that the default copy constructor is used, the program runs fine)
gShop::gShop(const gShop & cShop)
{
for(int i = 0; i < (int)vCustomer.size(); ++i)
{
vCustomer[i] = cShop.vCustomer[i];
}
}
Thanks
Note I also have an assigned operator
gShop gShop::operator=(const gShop & rhs)
{
if (this == &rhs) return *this;
for(int i = 0; i < (int)vCustomer.size(); ++i)
{
delete vcustomer[i];
vCustomer[i] = new gCustomer;
vCustomer[i]= rhs.vCustomer[i];
}
}
You've implemented your copy constructor and assignment operator wrongly, they are doing shallow copies not deep copies, and they don't resize the target vector. Here's a deep copy copy constructor
gShop::gShop(const gShop & cShop)
{
for(int i = 0; i < (int)cShop.vCustomer.size(); ++i)
{
if (cShop.vCustomer[i])
vCustomer.push_back(new gCustomer(*cShop.vCustomer[i]));
else
vCustomer.push_back(NULL);
}
}
and here's a deep copy assignment operator
gShop& gShop::operator=(const gShop & rhs)
{
if (this == &rhs) return *this;
// clear any existing data
for(int i = 0; i < (int)vCustomer.size(); ++i)
delete vcustomer[i];
vcustomer.clear();
// add the new data
for(int i = 0; i < (int)rhs.vCustomer.size(); ++i)
{
if (rhs.vCustomer[i])
vCustomer.push_back(new gCustomer(*rhs.vCustomer[i]));
else
vCustomer.push_back(NULL);
}
return *this;
}
Basically the problem was that the you were copying pointers instead of allocating new memory. If you want a deep copy it's essential you allocate new memory.
Of course there is the bigger question, why are you using a vector of pointers at all. One of the big advantage of a vector is that you no longer have to explicitly manage memory, by using a vector of pointers you have lost that benefit. I don't know you program but it seems to me that std::vector<gCustomer> would be better than std::vector<gCustomer*>. With std::vector<gCustomer> you don't need to write a copy constructor or assignment operator, the deep copy will happen automatically (assuming gCustomer does a deep copy).
The loop
gShop::gShop(const gShop & cShop)
{
for(int i = 0; i < (int)vCustomer.size(); ++i)
{
vCustomer[i] = cShop.vCustomer[i];
}
}
uses the wrong limit. It should run from 0 to the length of the existing object:
i < (int)cShop.vCustomer.size()
std::vector<> has copy constructor itself to do the deep copy.
So the copy constructor the compiler gives will do the work you want actually.
If you want to implement it yourself, it's better in the init list of the constructor of gShop.
gShop::gShop(const gShop & cShop):vCustomer(cShop.vCustomer)
{
}
The size of vCustomer is always zero in your copy ctor.
I'm confused with your operator=(). What do you want?
I suggest you read some text books about like c++ primer.
Here is an example of a class that is made available for the + operation.
class A
{
public:
int *array;
A()
{
array = new int[10];
}
~A()
{
delete[] array;
}
A operator+ (const A &b)
{
A c;
for(int i=0; i<10; i++)
c.array[i] += array[i] + b.array[i];
return c;
}
};
int main()
{
A a,b,c,d;
/* puts some random numbers into the arrays of b,c and d */
a = b+c+d;
}
Will a run the destructor before copying the result of b+c+d or not? If not, how do I make sure no memory is leaked?
The + operator overload is designed this way such that no operand is modified.
You need to add an equals operator to A. Also, you will likely want to create a copy constructor.
When a becomes the return from b+c+d, the array pointer in a gets over written without delete[] ever being called on it. You need to make an operator= that deletes the array.
An example of an operator= is below:
A& operator=(A const& a)
{
if (&a != this) {
int* tmp = this->array;
this->array = new int[10];
//copy a.array to this->array
delete[] tmp;
}
return *this;
}
There's a lot of subtleties in this if you're new to operator=.
In particular, the check whether or not a is equal to this is necessary because it's perfectly valid to write:
A a;
a = a;
This would cause a pointless copy, and in most cases of operator= will cause bugs.
The other subtlety is less of a requirement than that one and more of a coding style (though a very wide spread standard). When copying something that is dynamically allocated, you always want to allocate and copy before you release. That way, if new throws an exception (or something else fails), the object is still in a stable state, though with it's old data instead of the new expected dated.
Will this cause memory leak?
Yes, it will. You forgot to add copy constructor and assignment operator. See Rule of Three
You could also use std::vector<int> for A::array instead of int*. In this case you wouldn't need to worry about copy constructor/assignment operator (as long as you don't add something else that must be handled in destrcutor).