I wonder whether copying a vector I am copying the vector with its values (whereas this is not working with array, and deep copy need a loop or memcpy).
Could you hint to an explanation?
Regards
You are making a deep copy any time you copy a vector. But if your vector is a vector of pointers you are getting the copy of pointers, not the values are pointed to
For example:
std::vector<Foo> f;
std::vector<Foo> cp = f; //deep copy. All Foo copied
std::vector<Foo*> f;
std::vector<Foo*> cp = f; //deep copy (of pointers), or shallow copy (of objects).
//All pointers to Foo are copied, but not Foo themselves
Vector will resize to have enough space for the objects. It will then iterate through the objects and call the default copy operator for every object.
In this way, the copy of the vector is 'deep'. The copy of each object in the vector is whatever is defined for the default copy operator.
In examples... this is BAD code:
#include <iostream>
#include <vector>
using namespace std;
class my_array{
public:
int *array;
int size;
my_array(int size, int init_val):size(size){
array = new int[size];
for(int i=0; i<size; ++i)
array[i]=init_val;
}
~my_array(){
cout<<"Destructed "<<array[0]<<endl;
if(array != NULL)
delete []array;
array = NULL;
size = 0;
}
};
void add_to(vector<my_array> &container){
container.push_back(my_array(4,1));
}
int main(){
vector<my_array> c;
{
my_array a(5,0);
c.push_back(a);
}
add_to(c);
//At this point the destructor of c[0] and c[1] has been called.
//However vector still holds their 'remains'
cout<<c[0].size<<endl; //should be fine, as it copies over with the = operator
cout<<c[0].array[0]<<endl;//undefined behavior, the pointer will get copied, but the data is not valid
return 0;
}
This is BETTER code:
#include <iostream>
#include <vector>
using namespace std;
class my_array{
public:
int *array;
int size;
my_array(int size, int init_val):size(size){
cout<<"contsructed "<<init_val<<endl;
array = new int[size];
for(int i=0; i<size; ++i)
array[i]=init_val;
}
my_array(const my_array &to_copy){
cout<<"deep copied "<<to_copy.array[0]<<endl;
array = new int[to_copy.size];
size = to_copy.size;
for(int i=0; i<to_copy.size; i++)
array[i]=to_copy.array[i];
}
~my_array(){
cout<<"Destructed "<<array[0]<<endl;
if(array != NULL)
delete []array;
array = NULL;
size = 0;
}
};
void add_to(vector<my_array> &container){
container.push_back(my_array(4,1));
}
int main(){
vector<my_array> c;
{
my_array a(5,0);
c.push_back(a);
}
add_to(c);
//At this point the destructor of c[0] and c[1] has been called.
//However vector holds a deep copy'
cout<<c[0].size<<endl; //This is FINE
cout<<c[0].array[0]<<endl;//This is FINE
return 0;
}
Related
I try to free the memory correctly after the program ends, but I always encounter a problem.
In my code I want to have an array of all the numbers that I allow in my program, and have objects A and B (or more) that each one have some of the numbers that I allowed.
In the end I want to delete 'a' and 'b' only after "ints" getting out of the scope. But A and B calls their distructors to delete some of ints variables.
#define MAX_LEN 255
class IntArray
{
public:
int len;
void add(int* n) {
arr[len] = n; len++;
}
IntArray() : arr(new int* [MAX_LEN]), len(0) {}
~IntArray() {
for (int i = 0; i < len; i++)
delete arr[i];
delete[] arr;
}
private:
int** arr;
};
class Object
{
public:
void add(int* n) {
myIntArr.add(n);
}
private:
IntArray myIntArr;
};
int main(void)
{
int* a = new int(5);
int* b = new int(6);
IntArray ints;
ints.add(a);
ints.add(b);
Object A;
A.add(a);
Object B;
B.add(b);
return 0;
}
If you want to share dynamically allocated ints between multiple objects, use std::shared_ptr<int>.
Also, rather than writing a dynamic array type yourself, use std::vector to do it (correctly) for you.
using int_ptr = std::shared_ptr<int>;
class IntArray
{
public:
void add(int_ptr n) {
arr.push_back(n);
}
private:
std::vector<int_ptr> arr
};
class Object
{
public:
void add(int_ptr n) {
myIntArr.add(n);
}
private:
IntArray myIntArr;
};
int main(void)
{
int_ptr a = std::make_shared<int>(5);
int_ptr b = std::make_shared<int>(6);
IntArray ints;
ints.add(a);
ints.add(b);
Object A;
A.add(a);
Object B;
B.add(b);
return 0;
}
If you just want to have a copyable array of int, use std::vector<int>.
You're deleting a and b twice.
You should only delete something returned by new and exactly once.
But you add them both to IntArray ints; and then one each to Objects A and B and their destructors delete them also. Destructors are called in reverse order to it's when ints is destructed you'll be deleting them again - that's "Undefined Behaviour" but normally a catastrophic failure (crash) either immediately or later during executon.
The shortest fix is:
int* a = new int(5);
int* b = new int(6);
int *ac = new int(*a);//copy of *a
int *ab = new int(*b);//copy of *b
IntArray ints;
ints.add(a);
ints.add(b);
Object A;
A.add(ac);
Object B;
B.add(bc);
But it's not clear what our intention is. IntArray isn't an array of int as it stands, it's an array of pointers to int (which have been allocated by new).
My 'fix' will mean if you modify a (e.g. *a=20) you won't modify the copy (ac) added to the Object A.
I got stuck with deleting an dynamically allocated array of int.
I've got a destructor, where I'm trying to use a loop for to delete all elements of array and finally delete it.
I have code on http://rextester.com/OTPPRQ8349
Thanks!
class MyClass
{
public:
int _a;
int* c;
int fRozmiar;
static int fIlosc;
MyClass() //default constructor
{
_a=0;
c = new int [9];
for(int i = 0; i<=9; i++)
{
c[i] = 1;
}
fIlosc++;
}
MyClass(int a1, int c1) // parametrized constructor
{
_a=a1;
c = new int [c1];
for(int i = 0; i<=c1; i++)
{
c[i] = rand();
}
fIlosc++;
}
MyClass(const MyClass &p2) // copy constructor
{
_a =p2._a;
c = p2.c;
fRozmiar = p2.fRozmiar;
fIlosc = fIlosc;
fIlosc++;
}
~MyClass(); // destructor
static int getCount() {
return fIlosc;
}
};
//Initialize static member of class
int MyClass::fIlosc = 0;
MyClass::~MyClass()
{
for(int i = 0; i<sizeof(c); ++i)
{
delete[] c[i];
}
delete[] c;
fIlosc--;
}
int main()
{
}
Remove the for-loop, but keep the delete[] c after it.
Each int doesn't need to be deleted because they're not dynamically allocated. If you needed to delete them, then the for-loop wouldn't work becuase: sizeof(c) is not the size of the array, and delete[] should have been delete instead.
There are several problems in the code.
First, that loop in the destructor must go. If you didn’t new it, don’t delete it.
Second, a loop through an array of N elements should be for (int i = 0; i < N; ++i). Note that the test is i < N, not i <= N. The loops as currently written go off the end of the array. That’s not good.
Third, the copy constructor copies the pointer. When the first object goes out of scope its destructor deletes the array; when the copy goes out of scope its destructor also deletes the array. Again, not good. The copy constructor has to make a copy of the array. In order to do that the class needs to also store the number of elements the array.
I am trying to swap the content in the arrays by swapping the pointers pointing to the two arrays.
My method is the same as what Daniel answered in this question: Swap arrays by using pointers in C++. But the difference is that my array will be a member in a class.
My code can be compiled successfully, but the output results are quite weird.
This is my header file:
#include <stdio.h>
#include <iostream>
class Map
{
public:
Map(int times); // Create an empty map (i.e., one with no key/value pairs)
int size(); // Return the number of key/value pairs in the map.
void dump();
void swap(Map &other);
int *retrieve();
void setptr(int *newptr);
private:
int *ptr;
int array_1[5];
};
Here is my implementation:
#include "Map.h"
#include <iostream>
using namespace std;
Map::Map(int times) {
for (int i = 0; i < 5; i++) {
array_1[i]=i*times;
}
ptr=array_1;
}
void Map::dump() {
ptr=array_1;
for (int i = 0; i < 5; i++) {
cout << *ptr << endl;
ptr++;
}
for (int i = 0; i < 5; i++) {
ptr--;
}
}
void Map::swap(Map &other) {
int *temp;
temp = this->ptr;
this->ptr = other.retrieve();
other.setptr(temp);
}
int *Map::retrieve() {
return ptr;
}
void Map::setptr(int *newptr) {
ptr=newptr;
}
Can anyone tell me what is wrong and how to implement it smartly?
The following code runs fine:
#include <stdio.h>
#include <iostream>
#include <conio.h>
using namespace std;
class Map
{
public:
Map(int times); // Create an empty map (i.e., one with no key/value pairs)
int size(); // Return the number of key/value pairs in the map.
void dump();
void swap(int &other);
int *retrieve();
void setptr(int *newptr);
private:
int *ptr;
int array_1[5];
};
Map::Map(int times){
for (int i=0;i<5;i++){
array_1[i]=i*times;
}
ptr=array_1;
}
void Map::dump(){
for (int i=0;i<5;i++)
{
cout<<ptr[i]<<endl;
}
}
void Map::swap(int &other){
int *temp;
temp=this->ptr;
this->ptr=&other;
other = *temp;
}
int *Map::retrieve(){
return ptr;
}
void Map::setptr(int *newptr){
ptr=newptr;
}
int main()
{
Map m(2);
Map n(3);
m.dump();
m.swap(*n.retrieve());
m.dump();
getchar();
}
1) Added a main function
2) Changed Swap function
But the problem that christopher pointed out will still persist i.e the pointer will point to an array in another object.
Edit: You probably need something like this:
void Map::swap(Map &other){
Map *temp;
temp=this;
*this = other;
other = *temp;
}
Map *Map::retrieve(){
return this;
}
Note: it is probably not elegant.
The problem with your design is that the pointer refers to an array in the same object.
Suppose you have to objects a and b. If you swap their pointers, a.ptr will point to b.array_1 which contains the data. reciprocally b.ptr will point to a.array1.
Unfortunately if one of the object -- say b -- gets destroyed (because it was a local object that goes out of scope, or for whatever reason) the pointer of the remaining object would point to an array which doesn't exist anymore. This is UB.
To solve your issue, you'd neet to allocate an array dynamically in the constructor. Get rid of array_1 completely:
Map::Map(int times){
ptr=new int[5]; // or better define a constant to avoid hard coded sizes
for (int i=0;i<5;i++){
ptr[i]=i*times;
}
}
Note that if you use pointers, you need to ensure the invarients on it. This means that you should define also the copy constructor and the assignment operator (to avoid the ptr to be blindly copied), as well as a destructor (to delete the dynamically allocated array).
P.S.: I suppose that you are learning C++ and are not yet familiar with vectors. These would avoid all the hassles here
Edit: if you experience your problem before any object is destroyed, it's because of a bad implementation of dump(): you increment the pointer there in, so that it will no longer point to the start of the array.
void Map::dump(){
for (int i=0;i<5;i++){
cout<<ptr[i]<<endl; // don't change ptr value !!
}
}
One simple trick to avoid such problems, is to systematically declare the member functions that are not supposed to change the state of the object as const:
class Map {
...
void dump() const;
...
}
Then the compiler issues an error if you try to accidentally change a member.
I'm trying to create a vector of structs which each have a an array of pointers. However, I can't seem to delete the vector without a memory problem.
When I run valgrind
==29801== Invalid free() / delete / delete[] / realloc()
==29801== at 0x4A05A36: operator delete (vg_replace_malloc.c:515)
==29801== by 0x4009D4: test_struct::~test_struct() (in /home/hltcoe/rcotterell/code-switching/a.out)
==29801== by 0x40142B: void std::_Destroy(test_struct*) (in
/home/hltcoe/rcotterell/code-switching/a.out)
==29801== by 0x401299: void std::_Destroy_aux::__destroy(test_struct*,
test_struct*) (in /home/hltcoe/rcotterell/code-switching/a.out)
EDIT
#include <vector>
using namespace std;
struct test_struct {
public:
int * array;
test_struct() {
array = NULL;
}
~test_struct() {
delete[] array;
}
private:
test_struct(const test_struct& that);
test_struct& operator=(const test_struct& that);
};
int main(int argc, char ** argv) {
vector <test_struct> data;
for (int i = 0; i < 5; ++i) {
test_struct tmp;
tmp.array = new int[5];
data.push_back(tmp);
}
}
And it gives the following compile error. Any ideas?
You should follow rule of three or Use STL container where ever possible:
struct test_struct
{
explicit test_struct(int size) : array(size) { }
std::vector<int> array;
};
int main()
{
vector <test_struct> data(5, test_struct(5));
return 0;
}
Your solution doesn't work because of test_struct destructor and the fact you're trying to store your structs in a vector.
When test_struct tmp is pushed to the vector, a copy of test_struct is created. Then tmp is destroyed with calling delete[] array and the copy in vector <test_struct> data ends up with a dangling pointer.
You probably need to rethink your architecture, or at least add a copy constructor for the test_struct that will copy the whole array
I'm trying to create my own version of an array called a safearray, to test my knowledge of operator overloading and creating proper class's and such.
I'm encountering two errors.
SafeArray.h:11:15: error: ‘const int SafeArray::operator’ cannot be overloaded
SafeArray.h:10:10: error: with ‘int& SafeArray::operator’
My code is split between three files.
Main.cpp
#include <cstdlib>
#include <iostream>
#include "SafeArray.h"
using namespace std;
int main(int argc, char** argv) {
SafeArray a(10); // 10 integer elements
for (int i = 0; i < a.length(); i++) {
cout << i << " " << a[i] << "s" << endl; // values initialise to 0
}
cout << endl << a[1]; // Program exits here.
a[3] = 42;
cout << a[3];
a[10] = 10;
cout << a[10];
a[-1] = -1; // out-of-bounds is "safe"?
SafeArray b(20); // another array
b = a; // array assignment
for (int i = 0; i < b.length(); i++) {
cout << b[i] << endl; // values copied from a
}
return 0;
}
SafeArray.h
#ifndef SAFEARRAY_H
#define SAFEARRAY_H
class SafeArray {
public:
SafeArray(int); // int variable will be the array size
int length();
int boundsCheck(int y); // constructor will call this function
// const SafeArray operator= (const SafeArray&);
int& operator[] (int y);
const int operator [] (const int y); // you need this one too.
SafeArray &operator=(SafeArray rhs) {
std::swap(array, rhs.array);
std::swap(length_, rhs.length_);
}
SafeArray(SafeArray const &other);
~SafeArray();
private:
int length_;
int *array;
//int array[];
};
#endif /* SAFEARRAY_H */
SafeArray.cpp
#include "SafeArray.h"
#include <iostream>
SafeArray::SafeArray(int x) {
length_ = x;
array = new int[length];
for (int i = 0; i < length_; i++) {
array[i] = 0;
}
}
int SafeArray::length() {
return this->length_;
}
int SafeArray::boundsCheck(int y) {
}
int& SafeArray::operator[] (int y) {
return array[y];
}
SafeArray::~SafeArray() {
delete [] array;
}
SafeArray::SafeArray(SafeArray const &other) {
int *temp = new int[rhs.size_];
for (int i=0; i<rhs.size_; i++)
temp[i] = rhs.array[i];
std::swap(temp, array);
delete [] temp;
return *this;
}
Your class definition isn't valid. int array[] is an incomplete type, which must not appear as a (non-static) class member. Some compilers accept this as a synonym for int array[0], but zero-sized arrays are not valid in C++, either (only in C99).
In short, you cannot write your code the way you do. You need to learn about dynamic allocation and manage your own memory. Check out how std::vector is implemented.
In C++11, I might recommend a std::unique_ptr<int[]> array as a quick-fix approach, to be initialized as array(new int[x]).
Actually int array[] is valid, and may appear as a class member. The following compiles with strict C++11 conformance:
class foo
{
public:
foo() {}
int length;
int A[];
};
void ralph()
{
foo *bar = (foo *)new int[ 21 ];
bar->length = 20;
bar->A[0] = 1;
}
This is legal, and has its advantages (occasionally). Although it is not commonly used.
However, I suspect that the OP wanted something more along the lines of
class SafeArray {
public:
SafeArray(int); // int variable will be the array size
int length();
int boundsCheck(int y); // constructor will call this function
int& operator[] (int y);
const int operator [] (const int y) // you need this one too.
private:
int length_;
int *array;
};
along with
SafeArray::SafeArray(int x) {
length_ = x;
array = new int[length];
for (int i = 0; i < length_; i++) {
array[i] = 0;
}
}
As #Kerrek already pointed out, your class definition is clearly wrong (shouldn't compile).
To fix it, you want to change the definition to something like:
int *array;
Then in your default ctor you could use something like this:
SafeArray::SafeArray(unsigned size = 0)
: array(new int[size])
{
for (unsigned i=0; i<size; i++)
array[i] = 0;
}
Then, yes, you'll need to write an assignment operator. The usual way is called the copy and swap idiom. You create a copy, then swap the contents of the current one with those of the copy:
SafeArray &operator=(SafeArray rhs) {
std::swap(array, rhs.array);
std::swap(length_, rhs.length_);
}
Along with that, you'll need a copy constructor that makes a copy of the data as well:
SafeArray::SafeArray(SafeArray const &other) {
int *temp = new int[rhs.size_];
for (int i=0; i<rhs.size_; i++)
temp[i] = rhs.array[i];
std::swap(temp, array);
delete [] temp;
return *this;
}
Finally, you'll need a destructor to destroy an object and (particularly) delete the memory it holds:
SafeArray::~SafeArray() {
delete [] array;
}
Then realize that all of that is an ugly mess that will never really work well. In particular, the basic methodology is restricted to an array that's basically fixed in size. As long as you only store ints, it's fairly easy to overlook the problems, and make a dynamic array that (sort of) works. When/if you want to store some other type, however, you just about need to separate allocating memory from initializing objects in that memory, which means throwing away essentially all the code above, and replacing it with something that:
keeps track of the array size and allocation size separately
allocates memory with ::operator new, an Allocator object, or something else similar
uses placement new to initialize objects in the memory when needed.
uses explicit destructor calls to destroy the objects
uses ::operator delete to release memory
and so on. To summarize, std::vector is not a trivial piece of work.
The error message refers to these two lines:
int& operator[] (int y);
const int operator [] (const int y); // you need this one too.
Your error message says that (int y) and (const int y) are too similar to be two different overloads of the [] operator. You cannot overload on (int y) and (const int y) because the calls would all be ambiguous.
You probably meant to return a const int if your SafeArray is const, but return an int& if your SafeArray is not const. In that case, you declare the second function to apply to const SafeArray, by putting the word const after the parameter list. This is what you should write in SafeArray.h:
int& operator[] (int y);
const int operator [] (int y) const; // you need this one too.
You would then have to write both of these functions in SafeArray.cpp:
int& SafeArray::operator[] (int y) {
return array[y];
}
const int SafeArray::operator[] (int y) const { // you need this one too.
return array[y];
}