My question might be a bit confusing because I really did not know how to word it. Essentially, I have three classes in total and two of them are holding double pointers of a type defined in the third class.
class Wheel
{
unsigned m_orderID{};
std::string m_name{};
};
The other two classes - Car & Truck - have identical private members:
class Car{
const Wheel** m_ptrToArray{};
size_t m_count{};
};
My problem is that, in main(), I am making changes to the Wheel class values, where the Truck needs to have values that change, but the Car class needs to remain unchanged. Because the Car and Truck are both using double pointers, any update to the Wheel values changes the output for both because they are accessing the information via address, if I'm understanding correctly.
I believe my problem lies in the constructor used to create a Car object:
Car::Car(const Wheel* wheels[], size_t count)
I am trying to make a copy of the Wheel array that's passed in, so that I can assign the copy to the Wheel** m_ptrToArray member. That way, if I update the original array in main(), only Truck will change and Car will remain untouched. But I can't figure out how to do it.
If I use the following code in my constructor:
m_ptrToArray = new const Wheel*[m_count];
for (auto i = 0u; i < m_count; i++)
{
m_ptrToArray[i] = wheels[i];
}
It doesn't do what I want, because I'm just assigning the address of the wheels array.
If I do:
Wheel* temp = new Wheel[m_count];
for (auto i = 0u; i < m_count; i++)
{
temp[i] = *wheels[i];
}
m_ptrToArray = new const Wheel*[m_count];
for (auto i = 0u; i < m_count; i++)
{
m_ptrToArray[i] = &temp[i];
}
It works correctly, changing Truck but not Car. However, I run into memory loss because I'm using the new keyword without delete - because I have no idea how to pass it other than &temp[i] which means if I delete it, it won't be able to reach the value any more.
I am at a loss. Hopefully, this explanation makes sense to somebody who knows what they are doing. Let me know if you need any more information at all!
You are on the right track. Truck can just save the Wheel* pointers it is given, while Car can make its own copy of the Wheel objects. Simply destroy those copies in Car's destructor. That seems to be the piece you are missing. Simply store the extra objects as an additional member of the Car class, instead of using a local variable in the constructor.
Try something like the following (FYI, I'm ignoring the need for copy/move constructors and assignment operators, per the Rule of 3/5/0. I'll leave that as an exercise for you to implement).
For Car:
class Car{
const Wheel** m_ptrToArray{};
size_t m_count{};
Wheel* m_wheels{};
public:
Car(const Wheel* wheels[], size_t count);
~Car();
};
...
Car::Car(const Wheel* wheels[], size_t count)
{
m_count = count;
m_wheels = new Wheel[count];
m_ptrToArray = new const Wheel*[count];
for (auto i = 0u; i < count; ++i)
{
m_wheels[i] = *(wheels[i]);
m_ptrToArray[i] = &m_wheels[i];
}
}
Car::~Car()
{
delete[] m_ptrToArray;
delete[] m_wheels;
}
Or simpler:
class Car{
const Wheel** m_ptrToArray{};
size_t m_count{};
public:
Car(const Wheel* wheels[], size_t count);
~Car();
};
...
Car::Car(const Wheel* wheels[], size_t count)
{
m_count = count;
m_ptrToArray = new const Wheel*[count];
for (auto i = 0u; i < count; ++i)
{
m_ptrToArray[i] = new Wheel(*(wheels[i]));
}
}
Car::~Car()
{
for (auto i = 0u; i < m_count; ++i)
{
delete m_ptrToArray[i];
}
delete[] m_ptrToArray;
}
And for Truck:
class Truck{
const Wheel** m_ptrToArray{};
size_t m_count{};
public:
Truck(const Wheel* wheels[], size_t count);
~Truck();
};
...
Truck::Truck(const Wheel* wheels[], size_t count)
{
m_count = count;
m_ptrToArray = new const Wheel*[count];
for (auto i = 0u; i < count; ++i)
{
m_ptrToArray[i] = wheels[i];
}
}
Truck::~Truck()
{
delete[] m_ptrToArray;
}
Related
I have a project in which I created an abstract class that represents a shape. I have a circle and a quad inherited from a shape and square inherited from a quad.
Finally I have a class called allShapes that has a polymorphic array of Shape ** pointers and its size.
I need to implement the + operator, which receives an allShapes object and returns a new allShape with all elements located at this and other.
When I copy the part of this, the copy is done correctly but when I copy the parts from other I think it does not copy because when the function is finished when it comes to destruction I jump to an error that I am trying to delete blank content. what did I do wrong?
allShapes allShapes::operator+(const allShapes & other) const
{
allShapes newS;
newS._size = (this->getSize() + other.getSize());
int k = 0;
newS._arr = new Shape*[newS.getSize()];
for (int i = 0; i < this->getSize(); i++)
{
newS._arr[i] = this->_arr[i];
}
for (int j = this->getSize(); j < newS.getSize(); j++)
{
newS._arr[j] = other._arr[k++]; //i think here is the problem
}
return newS;
}
edit: i add the others methods that someone asks:
allShapes::allShapes(const allShapes & other) //copy constructor
{
this->_size = other.getSize();
this->_arr = new Shape*[other.getSize()];
for (int i = 0; i < other.getSize(); i++)
{
this->_arr[i] = other._arr[i];
}
}
allShapes::~allShapes()//destructor to all elements
{
if (this->_arr != NULL)
{
for (int i = 0; i < this->_size; i++)
{
delete this->_arr[i];
}
delete[] this->_arr;
}
}
class allShapes {
private:
Shape ** _arr;
int _size;
what did I do wrong?
You used Shape ** to denote ownership of multiple Shape-derived objects, and copy the pointers. Whichever allShapes object that is destroyed first invalidates all the Shape *s in the other copy.
There are two possibilities for making it hard to go wrong. Either each allShapes has it's own copy of each Shape it holds, or they all share ownership. This is best expressed via a collection of either std::unique_ptr<Shape> for the former, or std::shared_ptr<Shape> for the latter.
class allShapes {
private:
std::vector<std::shared_ptr<Shape>> data;
public:
allShapes operator+(const allShapes & other)
{
allShapes copy = *this;
copy.data.insert(copy.data.end(), other.data.begin(), other.data.end());
return copy;
}
// compiler generated special members are correct
};
exp. I have a
class foo{
public:
int const * const array;
size_t const length;
}
There should be no changes on these variables after construction, including by any member methods, but the values should be accessible by everyone, so they should be constant.
But in the constructor, I need to decide the length first before I can initialize the array,
Besides, I need to call a function to allocate the memory location, instead of just a new, because this class is a bridge to a huge opaque data structure, and the memory is managed by that guy.(consider sth. like v8).
How can I initialize in this?
p.s. lets just call the allocator void * bar(size_t), and the constructor (maybe) looks like:
foo(size_t const len, int const *arr) {
this->array = reinterpret_cast<int *> (bar(len));
this->length = len;
for(size_t i = 0; i < len; i++) array[i] = arr[i];
}
You need to use the constructor's member initializer list like this:
class Test {
public:
Test(size_t length): length(length), array(func_to_allocate(length)) {}
size_t const length;
int const * const array;
};
Note: There is nothing in the body of the constructor {} all the initialization happens before it is run.
If, for some reason, you can't rearrange the members (or it's not helpful to do so), then this is the answer. If you can rearrange members and it's helpful to do so, then Galik's answer is superior.
It's hard to tell from your question, but I'm going to make a series of wild speculations, and then show the resulting code:
class bridge {
public:
int const * const array;
size_t const length;
bridge(const sourceType& source);
private:
static int const* init_array(const sourceType& source);
};
int const* bridge::init_array(const sourceType& source) {
int len = source.getLength();
int* arr = static_cast<int*>(bar(len));
fill(arr, len);
return arr;
}
bridge::bridge(const sourceType& source) :
array(init_array(source)),
length(source.getLength())
{}
That look viable?
You should initialize members in the initializer list:
foo(size_t const len, int const *arr)
: array(reinterpret_cast<int*>(bar(len))),
length(len)
{
for(size_t i = 0; i < len; i++) array[i] = arr[i];
}
You can help yourself get this right, using g++ -Weffc++, or your compiler's equivalent.
The following code constitutes a MCVE, this reproduces the problem I want to ask about but it's not the real code. The real code is quite more complicated so that's why I wrote this for a demonstration of the problem.
The key feature I am looking for is to be able to grow a dynamically allocated array, please do not suggest using the stl because it's explicitly forbidden. This code is for educational purpose and thus there are restrictions.
#include <cstring>
#include <iostream>
class Value
{
public:
Value(int value = 0);
Value(const Value &value);
Value &operator =(const Value &other);
~Value();
operator int() {return *m_absurdPointer;}
private:
int *m_absurdPointer;
};
Value::Value(int value) :
m_absurdPointer(new int[1])
{
*m_absurdPointer = value;
}
Value::Value(const Value &value)
{
m_absurdPointer = new int[1];
memcpy(m_absurdPointer, value.m_absurdPointer, sizeof(*m_absurdPointer));
}
Value &Value::operator =(const Value &other)
{
m_absurdPointer = new int[1];
memcpy(m_absurdPointer, other.m_absurdPointer, sizeof(*m_absurdPointer));
return *this;
}
Value::~Value()
{
delete[] m_absurdPointer;
}
class ValueArray
{
public:
ValueArray();
~ValueArray();
void append(const Value &value);
void show() const;
private:
Value *m_array;
unsigned int m_capacity;
unsigned int m_length;
};
ValueArray::ValueArray() :
m_array(nullptr)
, m_capacity(0)
, m_length(0)
{
}
ValueArray::~ValueArray()
{
delete[] m_array;
}
void
ValueArray::append(const Value &value)
{
if (m_length >= m_capacity)
{
Value *newarray;
unsigned int unitSize;
unitSize = 1;
newarray = new Value[m_capacity + unitSize];
if ((m_capacity > 0) && (m_array != nullptr))
memcpy(newarray, m_array, m_capacity * sizeof(*m_array));
delete[] m_array;
m_array = newarray;
m_capacity += unitSize;
}
m_array[m_length++] = value;
}
void
ValueArray::show() const
{
for (size_t i = 0 ; i < m_length ; ++i)
std::cout << static_cast<int>(m_array[i]) << std::endl;
}
int
main(void)
{
ValueArray example;
for (int i = 0 ; i < 10 ; ++i)
example.append(Value(i));
example.show();
return 0;
}
It causes as you can see a double free issue, because the delete[] m_array; calls the destructor of the class Value after it has copied the values to the re-newed array.
I tried to do this with malloc()/realloc() but I need the destructor of Value() to be called so new is mandatory because I can't use free().
How to prevent this?, if I remove the delete[] m_absurdPointer; the double free would be gone of course but there would be a memory leak.
You basically want to implement an own vector class, right?
OK, first things first: As far as I know you cannot grow previously allocated memory. At least not with the standard allocator.
So you need to allocate a new, larger chunk of memory.
You can do this the standard way, using new:
Type * newdata = new Type[size];
In this case the constructor of the class Type will be called for each new element, which is size times.
To get your old data into that new array you need to copy or move it there:
for (size_t it = 0; it < oldsize; ++it) {
newdata[it] = olddata[it];
// newdata[it] = std::move(olddata[it]);
}
This is what std::copy resp. std::move are doing. (You could also use std::swap inside a loop.)
For that to work the Type class needs both a default constructor and a valid implementation of copy or move assignment.
You're using memcpy. In C++, this is generally a bad idea: Your implemented assignment operator isn't called, Therefore both the objects in your old array and the raw copies are using the same pointer, which is why you get that double free, obviously.
You could also allocate raw memory and use placement new to copy or move construct the new objects from the old ones:
void * memory = new char[size * sizeof(Type)];
for (size_t it = 0; it < oldsize; ++it) {
new (memory + it * sizeof(Type)) Type(olddata[it]); // copy
}
The above is only an example, for real code you need to consider alignment, too.
Finally, I'm sure you can somehow trick the default allocator to free your (old) memory without destructing the objects within, this allowing you to use the raw copy memcpy made. Though this would be a hack and could break on complex classes, it's not the C++ way of doing this.
The idiomatic way is to copy or move the old objects to the new storage (with either assignment or construction).
You should use the move-constructor if you have to stick with an vector-like implementation of ValueArray:
class Value
{
public:
Value(int value = 0);
Value(const Value &value);
Value(Value&& val);
Value &operator =(const Value &other);
Value &operator =(Value&& other);
~Value();
operator int() {return *m_absurdPointer;}
private:
int *m_absurdPointer;
};
Value::Value(Value&& o) : m_absurdPointer(o.m_absurdPointer) {
o.m_absurdPointer = nullptr;
}
Value &operator =(Value&& o) {
delete[] this->m_absurdPointer;
this->m_absurdPointer = o.m_absurdPointer;
o.m_absurdPointer = nullptr;
}
void
ValueArray::append(const Value &value)
{
if (m_length >= m_capacity)
{
Value *newarray;
unsigned int unitSize;
unitSize = 1;
newarray = new Value[m_capacity + unitSize];
if ((m_capacity > 0) && (m_array != nullptr)) {
std::move(m_array, m_array + m_length, newarray);
}
delete[] m_array;
m_array = newarray;
m_capacity += unitSize;
}
}
I have a class with member of dynamic array (I posted my code in pastebin) I wonder if my both classes are correct and if there is anything wrong with them? Also how I need to write (41 line) a function that sets class Student to StudentsDynamicArray?
Here is my array class
class StudentsDynamicArray{
private:
Student *Stud;// students dynamic array
int n; // current size of array
int nmax; // max size of array
public:
StudentsDynamicArray():n(0), Stud(NULL), nmax(0){}
StudentsDynamicArray(int n):
n(n){}
~StudentsDynamicArray(); // destructor
void SetStud(Student S){Stud[n++] = S;} // I get error when I try to set String or Int. //How I need to change this?
Student GetStud(int i){return Stud[i];}
void IncreaseSize(int ns);
};
//Function which change size of array
void Student::ChangeSize(int kiek){
if(kiek > nmax){
int *SNEW = new int[kiek];
for(int i=0; i<n; i++)
SNEW[i] = mark[i];
delete [] mark;
mark = SNEW;
nmax = kiek;
}
else if(kiek < nmax){
int *SNEW = new int[kiek];
for(int i=0; i<n; i++)
SNEW[i] = mark[i];
delete [] mark;
mark = SNEW;
n = nmax = kiek;
}
}
In Student class you have
int *mark
this is indication that you must apply rule of three: copy constructor, assignment operator and destructor. First two should make a copy of dynamic array, and destructor should free memory. You have only destructor properly implemented. The same applies to StudentsDynamicArray class.
as for:
void SetStud(Student S){Stud[n++] = S;}
from your code it looks like you have not called IncreaseSize initially, and Stud is possibly NULL once you call SetStud. You can fix it by calling it in constructor:
StudentsDynamicArray():n(0), Stud(NULL), nmax(0){ IncreaseSize(10); }
also, all of the above would not be necessary if you would use std::vector, so if such low lever dynamic arrays are not your requirement then use std::vector. Then you would use rule of zero.
[edit]
Your copy constructor / assignment operators should like as follows:
Student (const Student& other) {
// here allocate this->mark to be the same size as in other
// copy values from other.mark to this->mark
// also copy other values from `other` to this
}
Student& operator= (const Student& other)
{
// here allocate this->mark to be the same size as in other
// copy values from other.mark to this->mark
// also copy other values from `other` to this
return *this;
}
As you can see they are "quite" similar. Wiki on rule of three is actually updated to C++11, where you can add move semantics which increase efficiency of copy by value operations (and actually are known as rule of five). If you are learning basics, you can stay with previous rules.
You could better use std::vector to wrap the students into a collection. This will dramatically increase performance, and will lead to less errors.
Below is the prototype of classes you can use.
class Student
{
private:
string mName;
unsigned int mAge;
public:
Student() : mName(""), mAge(0) {}
Student( string name, unsigned int age ) : mName(name), mAge(age) {}
string get_name() const;
void set_name( string name );
unsigned int get_age() const;
void set_age( unsigned int age );
};
class Students
{
private:
vector<Student> mCollection;
public:
Students() : mCollection() { }
Students( vector<Student> &input_collection ) : mCollection( input_collection ) { }
Students &add_student( Student &s );
Students &remove_student( Student &s );
Students &remove_student( string name );
Students &remove_student( size_t index );
Students &edit_student( size_t index, string new_name, unsigned int new_age );
};
Hope this helps.
When i create a class I would like to be able to store an array in that class. Is this possible?
For example. If i have a class called array to store an array from my main function
int main()
{
double nums[3] = {1 2 3}
array Vnums(nums)
return 0
}
class array
{
public
//constructor
array(double nums[])
{
double vector[] = nums;
}// end constructor
}// end array
Thank you!
use a std::array instead of a raw array. It's just like a raw array, but copiable, and has useful member functions.
class array
{
std::array<double, 3> classArray;
public:
//constructor
explicit array(const std::array<double, 3>& rhs)
:classArray(rhs)
{}// end constructor
}// end array
int main()
{
std::array<double, 3> nums = {{1 2 3}};
array Vnums(nums)
return 0
}
or maybe a std::vector if you want to be able to change the size at will
class array
{
std::vector<double> classArray;
public:
//constructor
explicit array(const std::vector<double>& rhs)
:classArray(rhs)
{}// end constructor
}// end array
int main()
{
std::vector<double> nums{1 2 3}; //C++11 feature
array Vnums(nums)
return 0
}
I'm not sure what you're doing, so it's hard to give solid advice. You can pass a raw array by reference, a pointer and a count, an iterator pair...
Yes, but you must either allocate the array dynamically upon class creation, or the array must always be the same size.
Option A:
class array{
private:
double* data;
unsigned size;
public:
array(double* d, unsigned s){
size = s;
data = new double[size];
for(unsigned i = 0; i < s; i++)
data[i]=d[i];
}
array(const array& copy){
double* temp = new double[copy.size];
delete [] data;
data = temp;
size = copy.size;
for(unsigned i = 0; i < size; i++)
temp[i]=copy.data[i];
}
array& operator= (const array& copy){
double* temp = new double[copy.size];
delete [] data;
data = temp;
size = copy.size;
for(unsigned i = 0; i < size; i++) data[i]=copy.data[i];
}
~array(){
delete[] data; // Don't forget the destructor!
}
};
This is probably the way you need, but note that you will almost certainly need the custom copy constructor and assignment operator so that you don't share any memory between multiple instances of this class. A better way might be to make a copy function that both can use.
Option B:
class array{
private:
double data[3];
public:
array(double* d){ //or "double(&d)[3]" to be safer, but less flexible
for(unsigned i = 0; i < 3; i++){
data[i] = d[i]; // If d is not at least size 3, your program will crash here (or later on, or maybe just act in an undefined way)
}
}
}
Haven't tested this, but it should be an ok starting point.