Consider the following class:
class Stats
{
private:
int arraySize; // size of array
int * data; // pointer to data, an array of integers
// default size of array in default constructor
static const int DEFAULT_SIZE = 10;
public:
Stats() // default constructor
{
arraySize = DEFAULT_SIZE; // element count set to 10
data = new int[DEFAULT_SIZE]; // array of integers
srand((unsigned int) time(NULL)); // seeds rand() function
// initializes integer array data with random values
for (int i = 0; i < DEFAULT_SIZE; i++)
{
// data filled with value between 0 and 10
data[i] = rand() % (DEFAULT_SIZE + 1);
}
}
~Stats() // destructor that deletes data memory allocation
{
delete [] data;
}
void displaySampleSet(int numbersPerLine)
{
cout << "Array contents:" << endl; // user legibility
// iterates through array and prints values in array data
for (int i = 0; i < arraySize; i++)
{
cout << data[i];
/* nested if statements that either prints a comma between
values or skips to next line depending on value of numbersPerLine */
if (i + 1 < arraySize)
{
if ((i + 1) % numbersPerLine != 0)
cout << ", ";
else
cout << endl;
}
}
}
}
For some reason, when I create a Stats object in the following way:
Stats statObject = Stats();
and then call the displaySampleSet() on it, the numbers display fine. However, the function prints garbage when the Stats object is created in the following way:
Stats statObject;
statObject = Stats();
I have no idea why it does this and have a feeling it has to do with the integer pointer 'data' and/or with the way the object is being created but I'm not sure what... Any and all help is fully appreciated! Thank you so much in advance.
Update: Destructor added
Both the code statements produce undefined behavior. It is lucky that the first one works while second doesn't.
Stats statObject = Stats();
Is copy initialization. It copies the created Stats object by calling the default constructor and then copies it in to statObject by calling the copy constructor. Note your class does not provide an copy constructor so the implicitly generated one gets used. This creates a shallow copy of the dynamically allocated members. The object from which this copy gets created is eventually destroyed and then whats left in your class is dangling pointers which do not point to anything valid. The copy constructor needs to do a deep copy.
Same is the case with:
Stats statObject;
statObject = Stats();
Wherein the compiler generated copy assignment operator is called. Resulting in similar problems as in case 1.
You need to follow the Rule of Three and provide your copy constructor and copy assignment operators for the class.
You code is using synthesized copy constructor , assignment operator defined by compiler.
Also you are not freeing the memory allocated dynamically for default constructor.
you have to define your own copy construcor, overload assignment operator( follow rule of three).
Both the cases are undefined behaviour
Stats statObject;
statObject = Stats(); //using compiler defined assignment operator.
Stats statObject = Stats(); //using compiler defined copy constructor.
Here temp object generated is is using assignment operator overloaded by compiler and is doing shallow copy instead of deep copy.
overload assignment operator:
Stats& operator=(const Stats& rhs);
define copy constructor:
Stats(const Stats& rhs);
Related
struct List {
int size;
int* items;
List& operator=(const List& l);
};
List& List::operator=(const List& l)
{
delete[] items;
size = l.size;
items = new int[20];
for (int i = 0; i < size; i++)
items[i] = l.items[i];
return *this;
}
ostream& operator<<(ostream& out, const List& l)
{
out << "Size: " << l.size << "\t {";
for (int i = 0; i < l.size; i++)
out << l.items[i] << " ";
return (out << "}");
}
int main()
{
int size = 6;
List l1 = { size, new int[size]{ 0, 1, 2, 3, 4, 5 } };
List l2 = l1;
l2.items[1] = 50;
cout << l1 << endl;
cout << l2 << endl;
return 0;
}
I'm confused since when I assign l2 = l1 by using an overloaded operator, why does the contents of l1 change, when altering l2 later on? Especially since l1 is passed as a const. They somehow point to the same object in memory instead of being a copy.
List l2 = l1; does not call the copy assignment operator (operator=(const List& l)). Since the "assignment" happens during a variable declaration you are instead initializing l2 via copy initialization which is calling the compiler generated default copy constructor. Since it does a shallow copy both objects will point to the same data.
If you are going to be writing your own classes that manage memory/resources you need to at least provide your own copy constructor, copy assignment operator and destrcutor. This is known as the rule of three. If you want to include move semantics then you need to provide a move constructor and move assignment operator, which is known as the rule of 5. There is also the rule of zero, where use use types that already "do the right thing" (like using a std::vector) allowing the compiler generated defaults to work for you and you can read about all it at The rule of three/five/zero
List l2 = l1;
Despite the =, because this is a declaration you're performing copy construction (formally "copy-initialisation"), which has nothing to do with the assignment operator.
You didn't define a copy constructor (which should look much like your copy assignment operator), so the pointers are indeed shared.
The results would have been what you expected, had you written:
List l2{};
l2 = l1;
By the way, I'd give size and items defaults (0 and nullptr respectively), if I were you. Otherwise, when you forget that {}, the members have unspecified values and all hell breaks loose. This could be done with a nice default constructor, or by forgetting this whole enterprise and using a std::vector instead ;)
List l2 = l1; calls the compiler-generated copy constructor, which does not deep copy the pointer member.
If you were to use a std::vector for items then you could leave the constructors and assignment operator to the compiler. (You don't have any delete[] calls in your code which means that your class will leak memory like a colander leaks water.)
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.
When does the object Second go out of scope? If I leave the third cout statement I get a compiler error. Even when I surround the initialization of Second and the cout statements.
#include <iostream>
using namespace std;
class MyArray{
public:
int Size;
int* data;
MyArray(int s) : Size(s), data(new int[s]){}
~MyArray(){
delete[](this->data);
}
};
int main()
{
MyArray First(20);
First.data[0] = 25;
MyArray Second = First;
cout << First.data[0] << endl;
cout << Second.data[0] << endl;
cout << Second.data[0] << endl;
system("PAUSE");
return 0;
}
The runtime error:
Here is what's going on: MyArray objects get a pointer to their own array of integers called data upon initialization. However, this line changes things for the Second object:
MyArray Second = First;
Now both First.data and Second.data point to the same int[] array, because the default copy constructor and assignment operator perform a shallow copy.
This is bad, because the compiler has freedom to destroy First after its last use in your code. Hence, accessing Second.data[0] may be invalid already.
Moreover, once First and Second go out of scope at the end of main(), they both will try deleting data. Only the first one would succeed; the second one will trigger undefined behavior.
To avoid such errors in the future, use the rule of three. It says that if you define a destructor, a copy constructor, or an assignment operator, you probably need all three of them.
Your code has only the destructor. Adding a copy constructor and an assignment operator will fix this problem, and prevent memory leaks on assigning MyArray objects.
The scope of both names ends at the }. (Scope is a compile-time notion.)
The lifetime of Second ends when execution reaches the end of the block, after which the lifetime of First ends. But your program has undefined behaviour because you delete the array twice, so anything can happen.
You need to do the deep copy using copy constructor
MyArray(const MyArray &t)
{
t.size = size;
if(size not_eq 0)
{
t.data = new int(size);
for(int i=0 ; i<size ; i++)
t.data[i] = data[i];
}
}
after this MyArray Second = First; will work fine
if you want to assign then you have to write assignment operator as well
if constructor is used for allocating memory.
In the following program it does not work like that.
See
#include <iostream>
using namespace std;
class Demo
{
int i;
public:
Demo()
{
cout<<"\nDefault contructor called";
}
Demo(int x)
{
i = x;
cout<<"\nParameter contructor called";
}
void Display()
{
cout<<endl<<"i = "<<i<<endl;
}
};
int main()
{
Demo *demo = new Demo[5]; // = {1,2,3,4,5};
int i;
cout<<endl<<endl<<"First time";
cout<<endl<<"Addresses are "<<endl;
for( i=0;i<5; i++)
{
cout<<endl<< &demo[i];
}
cout<<endl<<endl<<"first time assigning values";
for( i=0;i<5; i++)
{
demo[i]= i;
}
cout<<endl<<endl<<"\nAfter first assignment";
cout<<endl<<"Addresses are "<<endl;
for( i=0;i<5; i++)
{
cout<<endl<< &demo[i];
}
cout<<endl<<endl<<"Second time assigning values";
for( i=0;i<5; i++)
{
demo[i]= i+5;
}
cout<<endl<<endl<<" After Second assignment ";
cout<<endl<<"Addresses are "<<endl;
for( i=0;i<5; i++)
{
cout<<endl<< &demo[i];
}
for( i=0;i<5; i++)
{
demo[i].Display();
}
return 0;
}
Output:
Default contructor called
Default contructor called
Default contructor called
Default contructor called
Default contructor called
First time
Addresses are
0x8281008
0x828100c
0x8281010
0x8281014
0x8281018
first time assigning values
Parameter contructor called
Parameter contructor called
Parameter contructor called
Parameter contructor called
Parameter contructor called
After first assignment
Addresses are
0x8281008
0x828100c
0x8281010
0x8281014
0x8281018
Second time assigning values
Parameter contructor called
Parameter contructor called
Parameter contructor called
Parameter contructor called
Parameter contructor called
After Second assignment
Addresses are
0x8281008
0x828100c
0x8281010
0x8281014
0x8281018
i = 5
i = 6
i = 7
i = 8
i = 9
Here the constructor is called three time and the memory address are same, means it is not allocating new memory and uses the same address. Why?
Why is the constructor called more than one time?
When you do this:
demo[i]= i;
i is used to construct a temporary object using the constructor which takes an int. This temporary is then assigned to demo[i]. The assignment does not result in demo[i] being reconstructed as a new object with a different address (objects can't be reconstructed, and can never change their address), it simply results in a memberwise assignment from the members of the temporary to the members of demo[i] (unless you provide an assignment operator which does something different, which you didn't).
Thinking the constructor allocates the memory for your object is a fundamental misunderstanding. When the constructor is called you already have the memory allocated, either on the stack or on the heap. The constructor only prepares this memory for using it afterwards.
However, the constructor is often responsible for allocating further memory for resources used by your object, i.e.
class Container
{
public:
Container()
: ptr_(new int[5])
{}
// ...
private:
int* ptr_;
}
I have this below program, where I am passing a vector by reference to a function myFunc and inside this function, I am adding few elements to the vector.
I am not freeing object creating with new, for now ignore the memory leak due to this.
After myFunc() execution is complete I am printing the variables ctor and dtor
to know how many times constructor and destructor were called.
Output is:
Before Exiting 5 7
I am creating 5 objects so ctor is 5. But why is dtor 7? From where do the extra two counts? Am I missing something?
#include
#include
using namespace std;
static int ctor = 0;
static int dtor = 0;
class MyClass
{
public:
MyClass(int n)
{
i = n;
ctor++;
// cout << "Myclass ctor " << ctor << endl;
}
~MyClass()
{
dtor++;
// cout << "Myclass dtor" << dtor << endl;
}
private:
int i;
};
void myFunc(vector<MyClass> &m);
void myFunc(vector<MyClass> &m)
{
MyClass *mc;
for(int i = 0; i < 5; i++)
{
mc = new MyClass(i);
m.push_back(*mc);
}
}
int main()
{
vector<MyClass> m;
vector<MyClass>::iterator it;
myFunc(m);
cout << "Before Exiting " << ctor << " " << dtor << endl;
}
Vectors copy around objects, but only your int constructor increments ctor. That's not accounting for copy constructed objects, and because you didn't provide one, the compiler provided it for you.
Add
MyClass(const MyClass& rhs) i(rhs.i) { ++ctor; }
to your class to see if that balances the count.
Vectors start at a low size. When you push an element into them, they make a copy of it using the copy constructor so you don't see your normal constructor get called. When the vector size grows beyond its limit, it will increase its limit by a multiple of its current size (e.g. double it).
Vectors are guaranteed to always keep objects in contiguous memory, so if adding a new object exceeds the vectors capacity() (i.e. size() + 1 > capacity()), the vector allocates new memory somewhere and copies all elements into it. This will again use the copy constructor. So, your elements from the vector pre-resize will call their destructors after they are copied into the newly allocated space with their copy-constructor.
So, more destructor calls than normal constructor calls :)
Vectors sometimes call another constructor, copy constructor, which was implicitly generated by the compiler for your class. That's why some ctor++ calls are missing: not all objects were constructed with the constructor you defined, some were constructed with the other one.
To ensure correct behavior of a type (class) with vector, you must implement copy constructor for it:
MyClass(const MyClass& rhs) { i = rhs.i; ++ctor; } // copy constructor
...because the one generated by compiler does nothing.
As indicated by others, the reason behind your result is the copy constructor and resizing of the vector. A vector has both a size and a capacity. The capacity is normally always doubled when the vector has to resize to accommodate new elements so that resizing does not have to happen all that often.
Adding some trace code to print out the vector capacity between each push_back gives more clarity into this behaviour.
m.capacity(): 0
m.capacity(): 1
m.capacity(): 2
m.capacity(): 4
m.capacity(): 4
m.capacity(): 8
Before Exiting 5 7
What actually happens here is that the only time the destructor is called is when the vector is resized (see why below). The first time it's resized it has no elements, thus the destructor is never called. The second time, the capacity is 1 so the destructor is called once. The third time it's called twice and the fourth time it's called four times. This totals seven times called, just as the counter shows.
The elements dynamically allocated in myFunc are never deallocated, so the destructor never runs there, and the final printout ("Before Exiting...") is done before leaving the scope in which the vector is allocated, so the destructor for the last "vector reincarnation" isn't called until after that printout. Therefore, the destructor of MyClass is only called when the vector is resized.
§ 12.8.8 of the C++ standard says: If the class definition does not explicitly declare a copy constructor, there is no user-declared move constructor, and there is no user-declared move assignment operator, a copy constructor is implicitly declared as defaulted (8.4.2). Such an implicit declaration is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
Basically, since your struct violates the rule of five, the compiler made a copy constructor and assignment operator for you. That other constructor doesn't increment ctor, but uses the destructor you defined. The vector is then using that alternate constructor, as a speed improvement.
If you add protected: MyClass(const MyClass& b); to the class declaration, this problem goes away.