Array as a data member - c++

I want to have a class that contains an array as it's data member. The size of the array is declared during construction. I know that the size of an array needs to be known at compile-time, but is there no way to work around this by using a const int to define the size and subsequently using constructor initializer list? I'm not allowed to use vectors. This is my futile attempt:
#include <iostream>
using namespace std;
class myArray {
public:
myArray(int size) : capacity(size) {}
private:
const int capacity;
int a[capacity];
};
int main() {
myArray ar(25);
return 0;
}
It gives the following error:
'capacity' was not declared in this scope

Try to use pointer instead
#include <iostream>
using namespace std;
class myArray {
public:
myArray(int size) : capacity(size) {array = new int[capacity];}
~myArray() {delete [] array;}
private:
const int capacity;
int* array;
};
int main() {
myArray ar(25);
return 0;
}
And don't forget to release in the destructor.

All of the above answers are "technically correct", but none actually show the usage of a smart pointer which makes all of the additional memory management unnecessary.
class my_array
{
public:
my_array (size_t sz)
: arr {new int [sz]} {}
pirvate:
std::unique_ptr <int[]> arr;
};
Now you needn't concern yourself with the rule of (3 or 5), and the code is correct and (mostly) exception safe.

It could work with a static const but you would lose the constructor argument setting. You have to allocate it dynamically for it to work:
class myArray {
public:
myArray(int size) : capacity(size)
{
a = malloc(size * sizeof(int));
}
~myArray() { free(a); }
private:
int capacity;
int* a;
};
int main() {
myArray ar(25);
return 0;
}
Although allocating with new [] and freeing with delete [] is probably more C++ than malloc and free.
As stated in some comments, if you dynamically allocate and don't use automatic resource management objects like shared_ptr or unique_ptr, you have to follow the rule of three.

The problem with your original class is that it allows for different values to be passed in as the capacity. Hence, you cannot create an array with that value, in the way you want. It makes no difference that you only create one instance with a size of 25, that's a property of the program, and the class itself doesn't know it's only used that way.
Now I'm not going to question why you can't use a vector but it seems a shame to not use the full capabilities of the language/library.
However, given your restrictions, you can create an array dynamically rather than trying to create a fixed array:
#include <iostream>
using namespace std;
class myArray {
public:
myArray(int size) : capacity(size) {
a = new int[capacity];
}
~myArray() {
delete[] a;
}
// Also need all those other things, mandated by the
// rule of 3/5, to allow proper deep copy/move:
// - copy constructor.
// - copy assignment operator.
// - move constructor (C++11).
// - move assignment operator (C++11).
private:
const int capacity;
int *a;
};
int main() {
myArray ar1(25);
myArray ar1(42);
return 0;
}

By passing in capacity as a variable, you have a dynamically sized array (whose size is only known at runtime). This means that sizeof(myArray) is unknown to the compiler, which will cause problems.
Instead, you will need to store a pointer to an array.
Another (ugly) approach would be to use a placement strategy, place the array at the end of the struct/class, and give it a constant size of 1, as shown here

constness is insufficient, because you can even take a value that's provided by the user during execution of the program and make it const:
int x;
std::cin >> x;
const int y = x;
It's insufficient because the array dimensions must be known when the program is compiled.
Historically, if you were to initialise a static const "variable" at the point of declaration, then this would be enough to convince the compiler that it could use that value for an array dimension:
static const unsigned int N = 5;
because there's no way that can go wrong.
Nowadays, we have constexpr for the purpose of making this absolutely explicit.

You can use Template non-type arguments (only works for C++). Take a look at this page and example:
https://www.ibm.com/docs/en/zos/2.1.0?topic=arguments-template-non-type.

Related

Built-in array with variable size?

Chapter 2.3.2 of The C++ Programming Language lists this constructor:
class Vector {
public:
Vector(int s) :elem{new double[s]}, sz{s} { }
private:
double* elem;
int sz;
};
As far as I know, the array size must be a constant expression, and s isn't. Is this legal? If so, why?
Yes, it's legal because the array is allocated at runtime with the 'new' operator.
If you want to allocate an array at compile-time, you must provide a const int, or constant expression.
int count = 0;
cin >> count;
int* a = new int[count]; // This is dynamic allocation happen at runtime.
int b[6]; // This is static allocation and it happen at compile-time.

when trying to construct an Array of ints with a template for a Array class, Error: Why is the array type 'int [5]' is not assignable,

When trying to build an array with a template, I get an error when
implementing a default constructor for the Array class template.
int main()
{
Array<int,5> arrayOfFiveInts;
return 0;
}
template<typename T, size_t SIZE>
class Array
{
public:
Array<T,SIZE>::Array()
{
elements = new T [SIZE];
for (int i = 0; i < SIZE; i++)
{
elements[i] = 0;
}
}
private:
T elements[SIZE];
};
I am expecting to see the array created when Main runs.
You have to decide whether you want to have the array in automatic or dynamic memory.
If you meant to have it in automatic memory, you have to remove the call to new[] in the constructor since elements is already allocated when it is declared.
If you meant to have it in dynamic memory, you have to change the declaration of elements to
T* elements;
If you do so, then you also need to make sure your class follows the Rule of 3/5/0. Add a destructor:
~Array()
{
delete[] elements;
}
As well as implement (or delete) a copy/move constructor, and a copy/move assignment operator.
templates are header-only, you don't need to use the extra qualification:
Array<T,SIZE>::Array()
You have to define the class before the main() function in your example.
Check the correct format in
this Live demo.
Just remove this line
elements = new T [SIZE];
elements is an array not a pointer, so trying to allocate some memory and assign to it doesn't make any sense.

Set array field's length based on const integer field

Suppose I have a class...
class Foo
{
public:
Foo(int size);
private:
const int size;
int data[];
};
Supposing that the size field is set immediately at instantiation, how can I set the length of data based on that size input?
I would ordinarily use a std::vector here, but I am writing a library for Arduino, so that won't fly, and I'm trying to avoid external dependencies if I can.
You are out of luck here, as C++ must know the size of the array at compile time (in other words, the size must be a constant expression). Your options are to either use dynamic allocation
int* data;
then allocate with new int[size]; in the constructor initialization list, or, better, use std::unique_ptr<> (C++11 or later), which is a light wrapper around a raw pointer and deletes its allocated memory at scope exit, so you won't have to manually delete[]
class Foo
{
private:
std::unique_ptr<int[]> data; // no need to manually delete
public:
Foo(int size): data{new int[size]} {}
};
A third option is to make Foo a non-type template class (assuming you know the size at compile time, which is what it seems to be happening, at least judging from your question)
template<std::size_t size>
class Foo
{
int data[size];
public:
Foo()
{
// constructor here
}
};

Providing the size (const) of the member array as the constructor argument RELOADED

I have gone through some of the threads on the same topic but they are really advanced like set and all. For someone just finding his footsteps in C++, what would be the best way to deal with this?
The following code gives me error:
class AStack {
public:
AStack(int size) : max_Size(size) {
}
void push(int);
int pop();
int top();
bool isEmpty();
void Flush();
private:
const int max_Size;
int a[max_Size];
int index = -1; // Index of the top most element
};
You have 3 options here.
Turn the class into a template and the depth parameter becomes a template argument. Then it is constant and you can create the array with respective size.
Use a std::vector for your internal array and use the resize() method.
Make max_depth a static const uint32_t max_depth = 42; (Initialize in-class) and then you can use that max_depth, too as size for array a.
Solution 1 looks then like this:
template <size_t max_depth>
class AStack
{
// ...
int a[max_depth];
};
Solution 2 then looks like this:
#include <vector>
class AStack
{
public:
AStack( size_t max_depth )
{
a.resize(max_depth);
// ...
}
// ...
std::vector<int> a;
// ...
};
Solution 3 would look like that:
class AStack
{
static const int max_depth = 42;
int a[max_depth];
// ...
};
Fixed size c-arrays can only be declared with a constant array size expression.
The constructor affects the non static members of a class. The static const members of a class are "hard-coded" initialized.
So if you want to allow users of that class to use it with varying stack sizes, you need option 1 or option 2. If you want to hard-code the stack size within the class, use option 3. Option 2 can also be done "manually" using operator new() or new() instead of a std::vector. But then you have much more to type, to check, and you will most likely have bugs which will not impress your instructor ;)
Evidently this is a learning exercise, so the straight-forward solution with std::vector doesn't work for you. That means you need to use pointers and dynamic allocation.
Declare the array as a pointer instead:
int * a;
In the constructor, allocate the appropriate size array:
AStack(int size) : max_Size(size), a(new int[size])
And whenever you allocate memory, you need to free it when you're done with it. In this case, in the destructor.
~AStack() {
delete [] a;
}
Because the destructor is no longer trivial, the rule of three suggests that you need a copy constructor and assignment operator too. I'll leave that part to you.

Array declaration and size initialization (C++)

I'm not exactly sure how to pose this question so I'll start with some example code:
//header file
class A
{
public:
A();
private:
int x;
std::string arr[x];
}
//cpp file
class A
{
public:
A()
{
/*code to get the value of x from a cmd call*/
}
}
Is this code valid? More specifically, can I have my string array in my header file be of size x, even though x is not specifically given a value until an A object has been created?
If this doesn't work, is my only other option to use a dynamically allocated array?
The code is not valid. You should use a vector instead.
class A
{
public:
A();
private:
int x;
std::vector<std::string> arr;
};
A::A () : x(command_gets_x()), arr(x) {}
Since arr is being initialized by the value of x, the constructor only works when x precedes arr in A (as it is in your definition). However, if the only purpose of x is to track the size of the array, it is not necessary, since a vector has the size() method.
class A
{
public:
A() : arr(command_gets_x()) {}
int x () const { return arr.size(); }
//...
private:
std::vector<std::string> arr;
};
It's not valid. Array sizes must be constant expressions. Yes, you'll have to use dynamic allocation, though not necessarily directly. You can just use std::vector.
No, that's not possible, for one C++ doesn't have variable length arrays, and further, the array size must be a compile time constant.
You can in the constructor allocate an array with new, or, better use a std::vector.
No, you can't initialize arrays with non-const expressions. This will work, and is close to your original intent:
class A
{
...
const int x = 3;
std::string arr[x];
};
And in the .cpp file:
int A::x;
I found that on my mac, in x-code I could do the following
int x = foo() // get some value for x at runtime
int array[ x ];
but that is seriously uncool!! I just read yesterday that some compilers allow dynamic allocation on the stack, but I would recommend that you stay well clear of that.
If the value of x is not known until runtime, then you cannot allocate an array of size x until runtime. Think about what the compiler does: can an array of size x be allocated if we don't know how big x is? The only remaining option is to allocate at run-time (aka dynamically allocate).