I tried to initiallize my own int array's class but somthing in the operator overloading isnt working, for somereason my object's d'tor is being called before I want it to and it makes my code crash:
#include "stdafx.h"
#include "Array.h"
int _tmain(int argc, _TCHAR* argv[])
{
Array arr(6);
arr[3] = 6; //code crashes here
printf("%d\n", arr[0]);
return 0;
}
and now the class:
header file:
#pragma once
#include <malloc.h>
class Array
{
public:
//ctor dtor and operator overloading
Array(int);
Array(int *, int);
~Array();
Array operator[](int i);
//here memebers are defined
int *arr;
int size;
};
----cpp file---
#include "stdafx.h"
#include "Array.h"
//the function mallocs size for the array.
Array::Array(int g = 1) :size(g) //diplomatic value for size =1
{
this->arr = (int*)malloc(sizeof(int) * this->size);
}
Array::Array(int *p, int m_size=1) :arr(p), size(m_size)
{}
Array::~Array()
{
delete arr;
}
after this function my object is being deleted by the d'tor
Array Array::operator[] (int i){
for (int j=0; j < i; ++j)
{
++(this->arr);
}
return *this;
}
Because the operator[] function returns an Array object by value. That means when you do your assignment arr[3] = 6 the operator returns a copy of this, the compiler then implicitly creates an array object from the integer 6 (because it has a matching constructor) and suddenly your assignment looks like
arr[3] = Array(6);
Somewhere the temporary array objects (the one returned by the operator[] function, and the one the compiler creates on the right-hand side of the assignment) have to be destructed, but by then you have multiple object all using the same pointer arr inside themselves. The first temporary object is destructed and it deletes the pointer, leaving all other objects with an invalid pointer. Then the next temporary object is destructed and tries to delete the now invalid pointer. Boom, you have a crash.
There are two things you need to do to fix the problem. The first is to realize that the operator[] function returns the wrong type, it should return a reference to an element of the contained array instead:
int& Array::operator[](size_t const i)
{
return arr[i];
}
The second thing you need to do is to read about the rules of three, five and zero.
Also, to prevent the compiler from mistakenly construct an object from an int value, you should mark the constructor as explicit:
explicit Array(int);
The main problem
Regardless of other problems in your code, the signature of your operator[] is wrong:
Array Array::operator[] (int i){
...
}
You don't return an element of the array here ! You return a whole new array instead. So in your faulty statement, a new temporary Array gets constructed and gets destructed at the end of the enclosing expression.
Try this instead. At least it will not crash immediately:
int& Array::operator[] (int i){
return arr[i];
}
The cause of the crash
Unfortunately, you allocate arr using malloc(). Never do this in C++ but use new[] instead. This would not lead to a crash by itself. But in addition in your destructor you use delete.
It's either malloc()/free() or new/delete or new[]/delete[] Any other combination is undefined behavior (this causes the crash in your original code, when the temporary Array got destroyed).
Correct the problem as follows:
Array::Array(int g = 1) :size(g) //diplomatic value for size =1
{
arr = new int[size];
}
Array::~Array()
{
delete[] arr;
}
It will compile and work: online demo
Other serious issue
Finally to make the things even worse, your original indexing operator returns *this, making so a clone of your existing Array. This is now solved. But if you'd make any copy of your Array you'd have another problem:
This will cause the arr pointer to be used in two places. The first Array that gests destructed will invalidate the arr pointer that is used in the original Array. Any subsequent dereferencing of its arr would be undefined behavior.
It's not solved in my online demo, so I I'd strongly suggest to read about the rule of 3
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'm making a template for vector class in which I'm making vector from an array. But within the class the assignment operator is crashing the code as I can see the return value is some ( Process returned -1073741819 (0xC0000005)).
int i=0;
const int size = 4;
template<class T>
class vector{
public:
vector()
{
x = new T[size];
for(i=0;i<size;i++)
x[i] =0 ;
}
vector( T *a) // This part is creating issue if i comment it out code
// successfully return with value Zero. But with this it doesn't work at all.
{
// cout<<"called";
for(i=0;i<size;i++)
{
x[i] = a[i];
}
}
T operator *(vector &y)
{
T sum = 0;
for(i=0;i<size;i++)
{
sum += this->x[i] + y.x[i];
}
return sum;
}
private:
T * x; // Type of vector;
};
int main()
{
int arr1[4] = {2,3,4,5};
int arr2[4] = {6,7,8,9};
vector<int> v1;
vector<int>v2;
v1 = arr1; // This call is the culprit. it doesn't work t all.
v2 = arr2;
return 0;
}```
You have several issues that lead to the crash and other problems.
The first is that the statement
v1 = arr1;
is equivalent to
v1 = vector<int>(&arr1[0]);
That means a temporary vector<int> object will be created, which invokes the vector(T *a) constructor.
And here the problems start: Because it's a constructor, the object being constructed is uninitialized, as it's the purpose of the constructor to initialize it. That means the member variable x will also be uninitialized and will not be pointing to a valid location.
You need to allocate memory and make x point to that memory, otherwise you dereference the uninitialized pointer which leads to undefined behavior and very likely your crash.
As for the other problems, first of all you miss a destructor, which means that memory allocations don't have anywhere to be free'd. Which in this case actually is good, because since you don't have an actual assignment operator (and rely on the compiler generated assignment operator) the copying in v1 = arr1 will be a shallow copy, that copy only the pointer itself and not the memory it points to.
The shallow copy problem means that for a while you will have two objects whose x member will point to the same memory. And if one object deletes that memory (like when the temporary object mentioned above is destructed) then the second pointer in v1 will no longer be valid.
This is the reason behind the rules of three and five.
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
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).
Previous, I have the following code.
double* a[100];
for (int i = 0; i < 100; i++) {
// Initialize.
a[i] = 0;
}
The purpose of initialize array of a to 0 is that, when I iterative delete element of a, everything will work fine still even there are no memory allocated still for element of a.
for (int i = 0; i < 100; i++) {
// Fine.
delete a[i];
}
Now, I would like to take advantage of auto_ptr, to avoid having manual call to delete.
std::auto_ptr<double> a[100];
for (int i = 0; i < 100; i++) {
// Initialize. Is there any need for me to do so still?
a[i] = std::auto_ptr<double>(0);
}
I was wondering, whether there is any need for me to initialize auto_ptr to hold a null pointer? My feeling is no. I just want to confirm on this, so that there aren't any catch behind.
The C++03 specifies the constructor of auto_ptr as follows:
explicit auto_ptr(X* p =0) throw(); // Note the default argument
Postconditions: *this holds the pointer p.
This means that the below is perfectly well-formed. There is no need to initialize
auto_ptr<int> a = auto_ptr<int>();
std::auto_ptr's default constructor does the NULL-assignment for you -- or, as the standard (ISO/IEC 14882:1998) puts it, the constructor is declared as:
explicit auto_ptr(X* p =0) throw();
(X being the template parameter class, i.e., this is for std::auto_ptr<X>).
You can zero initialize all members of an array using:
double* a[100] = {0}; // is equivalent
An alternative to deletion by using for_each:
struct delete_object
{
template <typename T>
void operator()(T *ptr){ delete ptr;}
};
//later in the code...
std::for_each( &a[ 0 ], &a[ 0 ] + sizeof a / sizeof a[ 0 ], delete_object());
Now for your question:
whether there is any need for me to initialize auto_ptr to hold a null pointer?
There is no need to initialize the auto_ptrs array. The members will be default initialized if you leave it out.
However, note that auto_ptr may not be usable for its move semantics (copy of ownership) if you need to pass around the pointers to other functions. Also, in the coming standard auto_ptr is likely to be deprecated. Try using something like std::tr1::unique_ptr or std::tr1::shared_ptr (the latter is a reference counted smart pointer).