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.
Related
Say I have a struct (or class) with a dynamic array, its length, and a constructor:
struct array {
int l;
int* t;
array(int length);
};
array::array(int length) {
l=length;
t=new int[l];
}
I assume everything so far is legal: this is how I would write it (although it maybe not be the only way to do things), but I have seen the following code, that somewhat seems to work:
struct array {
int l;
int* t = new int[l];
array(int length);
}
array::array(int length) {
l=length;
}
It looks bad, so I wonder if this works out of sheer luck and undefined behaviors, or if there is some internal rule that makes this code work fine.
This code is not correct.
int* t = new int[l]; will happen before l=length;, thus reading the uninitialized variable l. Member initializers are handled before the constructor's body runs.
array::array(int length) : l{length} {}
instead would work because l is declared before t.
However, doing this "by hand" is a bad idea to begin with. You should be using std::vector.
The 2nd code snippet might have undefined behavior.
The data members are initialized at the order of how they're declared. For class array, when t is initialized l is not initialized yet. For objects with automatic and dynamic storage duration l will be initialized to indeterminate value, then the usage of l (i.e. new int[l]) leads to UB.
Note that l=length; inside the body of the constructor is just assignment; the initialization of data members has been finished before that.
BTW: With member initializer list the 1st code snippet chould be rewritten as
array::array(int length) : l(length), t(new int[l]) {
}
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
}
};
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.
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).
Im working on a template class which represents a managed Array.
E (*data)[];
data is my array.
data = new E[size];
And this, it doesn't like. it throws me;
cannot convert Component* to Component (*)[] in assignment
What gives?
Also can anyone explain why E is denoted with a * even though I didn't pass a pointer type into my template type?
E (*data)[];
data is a pointer to an array of E, not a pointer to E (and not to be confused with an array of pointer to E). There is a very appreciable difference.
EDIT: To help you understand...
new is returning to you a pointer to E, yet you have declared data as a pointer to array of E. An array is a type in C! It is not simply a pointer in disguise. An array may decay into a pointer in certain situations, but it doesn't go both ways.
EDIT 2:
Per your comment:
I was under the impression that I was creating a new array of E pointers?
Go to http://cdecl.org/
First, type in:
int (*data)[];
Read what it says. Now type:
int *data[];
Read again and note that it is not saying the same thing. One as a pointer to array of int, one is an array of pointers to int. Big difference.
If you want to dynamically allocate an array of pointers then data should be declared as:
E **data;
And then
data = new E*[size];
Just make your data member an E*. You will still be able to index array-style ala data[i] (that notation, when used with a pointer and integral value, basically means add the pointer and i times the size of the pointed-to objects).
EDIT:
template <typename E>
class X
{
X(int initial_size) : data_(new E[initial_size]), size_(initial_size) { }
X(const X& rhs) : data_(new E[rhs.size_]), size_(rhs.size_) { std::copy(...); }
~X() { delete[] data_; }
void capacity(int new_size)
{
E* p = new E[new_size];
std::copy(p, p + new_size, data_);
delete[] data_;
data_ = p;
size_ = size;
}
...
private:
E* data_;
int size_;
};