Set array field's length based on const integer field - c++

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
}
};

Related

unsafe template array constructor

This is probably a simple question but I have this template class:
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
The code is from a course pdf file where it says buff(new Type[n]) is unsafe. I don't understand why it's unsafe, isn't size_t generally unsigned? Can I have an example where it could have a compile and/or run-time error?
The code is "unsafe" in the fact that it relies on n being constructed before buff. This dependency adds brittleness to the code.
When you construct the members of the class they are constructed in the order the are declared in the class, not how they are called in the member initialization list, so if the code was changed to
template<typename Type>
class Array {
Type* buff;
size_t n;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Then when you do buff(new Type[n]), n is uninitialized and you have undefined behavior.
First of all the order, the initializations for the constructor are executed is not determined by the order they are written down, but by the order the initialized fields appear in the code:
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Here first n will be initialized and then buff.
class Array {
Type* buff;
size_t n;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
};
Now first buff will be initialized and then n, so n has no defined value in that case.
Using initialization lists for constructors is good practice, but be careful, that you don't create any assumptions on the order.
Generally it is a good idea to refrain from owning raw pointers. If you use smart pointers instead, you can not forget to release the data.
In the specific case, you might also want to use std::vector instead of a C-style array. That handles all the allocation, reallocation, releases, etc. in a thread safe manner for you. It seems like you are trying to write something like your own std::vector. Please only do that for educational purposes. Always prefer the standard implementation in production code. You probably won't get it better for quite a while. If you did, you would ask different questions here ;-)
First of all, you have a memory leak. But the question probably isn't about that. So let's assume you have a destructor that deallocates the array.
template<typename Type>
class Array {
size_t n;
Type* buff;
public:
Array(size_t n_): n(n_), buff(new Type[n]) {}
~Array() { delete[] buff; }
};
Now this particular code is perfectly safe. No exception can be thrown while assigning n_, the order of initialization is correct and buff is the only raw pointer in your class. However, as you start expanding your class and writing more classes, the risk of a memory leak increases.
Imagine that you need to add one more members to the class Array:
template<typename Type>
class Array {
size_t n;
Type* buff;
SomethingElse xyz;
public:
Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {}
~Array() { delete[] buff; }
};
If the constructor of SomethingElse throws, the memory allocated for buff will leak, because the destructor ~Array() will never be called.
Modern C++ calls pointers such as Type* buff raw pointers because you are responsible for deallocating storage yourself (taking exceptions into account), and introduces tools such as std::unique_ptr and std::shared_ptr that can take care of storage deallocation automatically.
In modern C++ you could write your class like this:
template<typename Type>
class Array {
size_t n;
std::unique_ptr<Type[]> buff;
public:
Array(size_t n_): n(n_), buff(new Type[n_]) {}
};
Notice the absence of a destructor. The unique_ptr will take care of calling delete for you.
Note also no dependency on class members inside the initializer list (simply writing new Type[n_] instead of new Type[n] makes your code more robust)
C++98 Standard 12.6.2.5.4 (I don't expect new versions to have relaxed this).
— Then, nonstatic data members shall be initialized in the order they
were declared in the class definition (again regardless of the order
of the mem-initializers).
So the order of initialization is defined according to this.
If you want an example of how to crash it simply make sizeof(Type)*n > total memory in your system.
It is not safe to call new operator inside initialization list.
if new fails the destructor of Array will not be called.
here is a similar question.
Are there any issues with allocating memory within constructor initialization lists?

Array as a data member

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.

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.

Use constructor to initialize array (C++)

How do I initialize an array through a constructor?
Here is the code for a class called Sort:
private:
double unpartitionedList[];
public:
Sort(double unpartitionedList[]);
Sort::Sort(double unpartitionedList[])
{
this->unpartitionedList = unpartitionedList;
}
I'd like to be able to pass an array to the constructor and have it stored in unpartitionedList[]. Like this: Sort(array[])
As the code is now, I get a compiler error in DevC++:
"[Error] incompatible types in assignment of 'double*' to 'double[0]'"
I've tried inserting reference (&) and dereference (*) operators where I thought they were needed, but unfortunately, my attempts were unsuccessful.
Any suggestions would be greatly appreciated.
Arrays aren't assignable. You'll have to do an element-wise copy or write actual C++ code and use std::array or std::vector.
class Sort
{
private:
double unpartitionedList[];
public:
Sort(double unpartitionedList[]);
};
Sort::Sort(double unpartitionedList[])
{
this->unpartitionedList = unpartitionedList;
}
That code will not compile as arrays are not assignable. You can accomplish your goal a few different ways (depending on the requirements you haven't mentioned).
Method 1: Manual Memory Management
class Sort
{
private:
double* unpartitionedList;
std::size_t _size;
public:
Sort(double* upl, std::size_t n);
};
Sort::Sort(double* upl, std::size_t n) : unpartitionedList(upl), _size(n)
{
}
There are a few things to note here: If you intend for this class to take ownership of the memory, you will have to properly manage it (e.g. free the memory in the destructor, and provide a proper copy-constructor that will perform a deep-copy). Because of the memory management requirements, this method is not recommended if not absolutely necessary.
Method 2/3: STD Containers
class Sort
{
private:
std::vector<double> _upl;
// or
// std::array<double, SIZE> upl; // with a constant SIZE defined
public:
Sort(const std::vector<double>& upl);
};
Sort::Sort(const std::vector<double>& upl) : _upl(upl)
// Sort::Sort(const std::array<double, SIZE>& upl) : _upl(upl)
{
}
This will remove the memory management requirement. std::vector will allow you to size the array at runtime. std::array is a thin wrapper around a C-style array (and must be sized at compile time).

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).