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.
Related
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;
}
I am working on some pointer practice in my C++ class and have gotten stuck working on problem, say I have a class like this :
class foo
{
public:
foo(char *inputWord = ""); // Default parameter constructor
private:
char * word;
int wordSize; // length of word
};
and in main I have :
int main()
{
foo baz("t");
foo bar("test");
return 0;
}
How would I make an implementation for this constructor?
So far I know that to initialize wordSize I could just do :
foo::foo()
{
wordSize = strlen(inputWord);
}
But I don't understand how to initialize the char *word.
I've tried the following:
strcpy(word,strlen);
for(int i =0; i< wordSize; i++)
word[i] = inputWord[i];
int count = 0;
while(count < wordSize+1)
{
inputWord = word;
inputWord++;
}
None of the above methods work. I even tried using memcpy() and memmove() but I just got stuck.
Has anyone dealt with this before and could point me in the right direction?
You can simply use strcpy just after having allocate memory:
Foo(char *inputWord) {
if (inputWord==nullptr) { // manage null pointer
wordSize=0;
word=nullptr;
return;
}
wordSize = strlen(inputWord); // store string length
word = new char[wordSize+1]; // allocate enough memory to store the original C-style string
strcpy(word,inputWord); // copy the original into destination
}
This is C-style, but you'd better use strings in C++:
class Foo {
private:
string word;
public:
Foo(const string &input) : word(input) {} // initialize word with inputWord (let C++ string type do all the tricky parts)
};
C++ string type is able to manage correctly (de)allocations, length, etc.
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 am confused about this library code..
what is the purpose of writing pointer in attributes name?
Like string *name;
this is the code:
#include <iostream>
using namespace std;
class stringlist
{
private:
int numstrings;
string *strings;
public:
stringlist() : numstrings(0), strings(NULL)
{}
// copy constructor
stringlist(const stringlist &other) :
numstrings(other.numstrings),
strings(new string[other.numstrings]) {
for (int i=0; i<numstrings; i++) {
strings[i] = other.strings[i];
}
}
// destructor
~stringlist() {
if (numstrings > 0) delete [] strings;
}
void printStrings();
const string& getString(int num);
void setString(int num,
const char *astring);
void setString(int num,
const string &astring);
int getNumstrings() {
return numstrings;
}
void setNumstrings(int num);
void swapStrings(stringlist &other) ;
// overloaded operators
stringlist& operator =
(const stringlist &other) {
stringlist temp(other);
swapStrings(temp);
return *this;
}
};
Can anyone please explain what is the purpose of
using string *strings
instead of string strings?
Thanks all!
string *strings;
declares a pointer to the first element of an array of strings. Because of C/C++ array/pointer semantics, you can treat this as an array of strings, e.g. index it as strings[n] to get the nth element.
string strings;
would just be one string. Since the class is for holding a list of strings, declaring just one string would not be sufficient.
In the code you gave, *strings is used for a dynamic array.
You can see that it's allocated with strings(new string[other.numstrings]) and the destructor deletes it if it's pointing to something.
More on dynamic arrays:
- http://www.cplusplus.com/doc/tutorial/dynamic/
Static array vs. dynamic array in C++
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.