I need to use array as a class property but I am not sure about the correct way. Assume that I have a class called A and I need an integer array in it.
First, if I want a static array with 10 elements, is the following way correct? If not, how should it be?
class A {
public:
int arr[10];
};
Second, if I want a dynamic array, which one of the followings is correct? If neither of them, I would be appreciated if you explain the reason and correct way.
class A {
public:
int *arr;
};
class A {
public:
int arr[];
};
Finally, what happens if I initialize a property in class definition as following
class A {
public:
int arr[] = {1,2,3};
// or
int *arr = new int[5];
// or
int number = 5;
};
Thanks a lot.
Note: I am aware of that it is much better to use vector or that kind of STL data structure but I need to stick to arrays somehow.
First, if I want a static array with 10 elements, is the following way correct?
Yes, that is a correct way.
Some people prefer to wrap the array in a class template such as std::array.
Second, if I want a dynamic array, which one of the followings is correct? If neither of them, I would be appreciated if you explain the reason and correct way.
class A {
public:
int arr[];
};
That is an ill-formed declaration. A member cannot be an array of indeterminate length.
class A {
public:
int *arr;
};
This is correct as such, but there is no dynamic array created. Using this for owning a dynamic array is bad design because bare pointers should never have ownership.
Even for pointing an array stored elsewhere, this is problematic since the class has no knowledge of the length of the array.
This would be a correct way, assuming the array is owned by the class:
class A {
public:
std::vector<int> arr;
};
If the array isn't owned by the class, then following is correct:
class A {
public:
std::span<int> arr;
};
std::span isn't in the standard until C++20 though, so until then you need to resort to a non-standard implementation.
Finally, what happens if I initialize a property in class definition as following
class A {
public:
int arr[] = {1,2,3};
That is ill-formed because the size of a member cannot be deduced from the initaliser.
int *arr = new int[5];
new int[5] is a default member initialiser. It will be used in case no initialiser is provided for the member otherwise.
For fixed-size arrays you can use int arr[10] or std::array<int, 10>.
For dynamically-sized or resizeable arrays if you cannot use std::vector or std::unique_ptr<int[]> you should use int* (int[] is not a valid data member type in C++) and implement constructor, copy constructor, move constructor, copy assignment, move assignment and destructor. See rule of five for more details.
Related
This is driving me around the bend. All the documentation on vectors is ridiculously deep.
In a class I want to declare a vector equal in length to the length of a string I've already declared. It sounds easy enough.
If I use:
class Test {
private:
size_t size = 10;
std::vector<int> array(size);
};
I get "only static const integral data members can be initialized within a class"
If I:
std::vector<int> array(anarray.length());
I get the fantastically unhelpful:
error C2061: syntax error : identifier 'anarray'
How do I do it?
In-class initialization requires use of the type var = value; syntax or the uniform initialization syntax.
For your needs, it is more appropriate to use type var = value; syntax.
class Test {
private:
std::size_t size = 10;
std::vector<int> array = std::vector<int>(size);
};
You can initialize size using:
std::size_t size{10};
However, if you use
std::vector<int> array{size};
it will try to create a std::vector with one element containing the value of size. That's not what you are interested in.
Judging by bits and pieces scattered across comments to different answers, I figured out that OP does not use C++11 and does not need dynamic array capability. Because of that, my advice is to go with good ol' C-style static arrays. Following code would do the trick:
struct Test {
static const size_t array_sz = 10;
int my_array[array_sz];
};
If, by nature of being an assignment from moronic CS teacher, an std::vector is mandated, this is how it should be initialized:
struct Test {
static const size_t array_sz = 10;
std::vector<int> my_array;
Test() : my_array(array_sz) {}
};
Explanation: std::vector needs to be constructed with a certain argument to specify it's size. Pre-C++11 the only way to call a specific constructor of a member variable was to do this in the so-called initializer list of the constructor of the enclosing class.
After C++11, this can also be done directly in site of member definition. But you need a compiler which is less than 6 years old for this.
Just declare std::vector<int> array and if you want you vector not be larger that one specific size create a method that check the vector size before insert.
Data members are usually initialized in the member initialization list:
class Test
{
std::vector<int> array;
public:
Test() : array(10)
{ // ^^^^^^^^^
}
};
Like this: (requires c++11 or better)
class Test {
private:
size_t size = 10;
std::vector<int> array = std::vector<int>(size);
};
Just get rid of the (size). The vector doesn't have, or have to have, a length until at least one instance of it exists. The number of elements in a vector is not part of its type. A std::vector<int> can contain no integers or a hundred integers -- it's still a std::vector<int>. When declaring that the vector is a member of the class, the number of members that instances of that vector will have, later when they exist, doesn't matter.
I have a class lets say aClass
class aClass
{
public:
int data;
aClass(): data(-1) { //default constructor
}
aClass(int x): data(x) { //int constructor
};
If I create an array like
aClass* arr=new aClass[10];
Question 1:
I was thinking no aClass object has been created since it is just a declaration of an array, but in a program that I tested it looks like arr[0] to arr[9] all points to a aClass object created by default constructor in its class? Because I had tested the program and the result is a[0].data=-1 all the way to a[9].data=-1.
Question 2: if objects are created with their default constructor, how am I supposed to create an array that has objects created by int constructor? obviously I can't write code like
aClass* arr=new aClass(2015)[10];
Quesntion 1: I was thinking no aClass object has been created since it is just a declaration of an array
You were thinking wrong. It's a declaration of a pointer which is initialized with the return value of new aClass[10] which constructs an array of 10 objects.
but in a program that I tested it looks like arr[0] to arr[9] all points to a aClass object created by default constructor in its class?
Yes, your observation is correct.
Question 2: if objects are created with their default constructor, how am I supposed to create an array that has objects created by int constructor?
Since c++11 you can use list initialization: aClass *foo = new aClass[3]{1, 2, 3}; as chris pointed in a comment.
You may want to use std::vector instead. There is std::vector::vector(size_type n, const value_type& val) for constructing copies and since c++11: std::vector::vector(initializer_list<value_type>) for different values.
With static/automatic arrays aggregate initialization was possible even before c++11:
aClass foo[3] = { 1, 2, 3 };
Instead of aClass* arr=new aClass(2015)[10];
You could do aClass *arr = new aClass[10]{2015, 2015/*etc*/}; if your compiler supports it.
In C++11, both of these mean the same thing.
aClass *arr = new aClass[10];
aClass *arr = new aClass[10]{};
Better alternative would be to use std::vector and avoid using raw pointers and instead use smart pointers ex. std::unique_ptr.
Example of this alternative could be:
int main()
{
vector<unique_ptr<aClass>> vec;
for (int i{0}; i != 10; ++i)
vec.emplace_back(new aClass(2015));
for (auto const& i : vec) // print data
cout << i->data << endl;
return 0;
}
Regarding question 1. The rules for initialization are very rigid. Your array new[] expression creates an array of values. Those values must be initialized and by default they use default initialization.
The answer to question 2 is that you can't do that with dynamic arrays. However, std::vector does provide a constructor that allows you to provide an object that will be copied to each of the contained elements. See explicit vector(size_type count, const T& value = T(), const Allocator& alloc = Allocator()).
If you want each element in the collection to be different, then you must use a loop and assign the elements individually unless the size of the array is determined statically in which case you can use initializer expressions. If you want to avoid the initial default construction for dynamic containers, then you should not store the objects by value. You should use a container of pointers or better yet a container of smart pointers. Then you can use direct initialization for each object.
You need to create array of pointers if you want to create an array without creating the objects. For example, aClass** arr = new aClass*[10]; creates an array of pointers. And when you want to create the actual objects, you need to do something like this: arr[0] = new aClass(2015);
I would like to know, if I have a class with an array attribute whose size is not the same for all instances :
class myObject{
private:
int size;
int* array;
// other methods/attributes
};
Is it obligatory allocated using new ?
explicit myObject(int size = 0):size(size){
array = new int[size];
}
Even if in the main(), I always use constant parameters to create the instances of the class ? (Meaning I know every array size at compile time).
int main{
myObject object (5);
return 0;
}
Apparently something like :
private:
int size;
int array[size];
wont work, no ?
That means that array attribute whose size are not constant of the class are obligatory on the heap ?
Thank you for your answers,
That class contains no array. What you called array is a pointer; you cannot store any ints in it. If you really do just store a pointer, you'll have to allocate the memory yourself somehow; it can't magically appear. You'll also have to deallocate it yourself, and make sure that copying and assigning myObject objects doesn't cause any issues.
However, it's unlikely that a pointer is really the best way to do things. The standard library provides the std::vector class template which lets you use almost exactly the syntax you want:
class myObject {
std::vector<int> vector;
public:
myObject() {};
explicit myObject(std::size_t n) : vector(n) {}
};
With this in place you can create myObjects and they'll have the right amount of storage ready for them. It'll likely be dynamically allocated using operator new[], just like if you'd do it manually, but you don't have to worry about copying or deleting it.
int main() {
myObject a; // default-constructed, vector is empty.
myObject b(10); // std::size_t constructor, vector has 10 elements.
} // scope exit, b and a destroyed.
You can use the vector member much like if it was an array; the only thing it does not support is implicit decay to pointer, but the data member function makes up for even that.
As an alternative, if you always know the size at compile-time you can change the class into a class template and make the size a template parameter:
template<std::size_t N>
class myObject{
std::array<int, N> array;
// other methods/attributes
};
However, note that you now cannot use myObject<10> to a function expecting myObject<20>.
It is unlikely that you want more control than the above possibilities provide -- std::vector can be given an allocator, so it can do almost all work for you -- you could use std::unique_ptr<int[]> and make_unique together to make things work for you. However, if you need this kind of power, you probably know it yourself.
As a closing note, if you're just learning C++ and your book doesn't cover std::vectors somewhere early on, perhaps it's best to get a different book; they're one of the most commonly-useful data structures in the standard library and definitely not something to be left in an appendix.
If you need a variable sized array as a member of a class, don't use built-in arrays directly. Instead, use std::vector<T>, e.g.:
class myObject {
std::vector<int> array;
public:
explicit myObject(int size = 0): array(size){}
};
You can get the std:vector<int>'s size using array.size(), i.e., there is no need to store the size separately. Also, the content is automatically default initialized.
Let's say I have a class that has a member which is an array. Is it possible to define its size upon construction/at run-time, in the following way:
class myClass {
private:
int myArray[n]
public:
myClass();
someOtherMethod();
};
Where n is a variable that is defined based on user input. If not, what would be the best alternative?
It depends.
Semantically, there are 3 types of arrays:
arrays with a size fixed at compile time
arrays with a size fixed at runtime
arrays with a dynamic size
C++ directly supports the first and third cases, respectively with regular arrays and the std::vector class.
C also supports the second type with two constructs:
variable length arrays (on the stack)
the oldie struct hack or tail-padding
I would advise, in C++, using the std::vector class in your case. It provides more than what you need, but is simpler to use.
On the other hand, you can still use tail-padding, even in C++. It does require careful engineering though.
Use a vector.
class myClass {
private:
std::vector<int> myArray;
public:
myClass();
someOtherMethod();
};
myClass::myClass (int size)
: myArray (size)
{
...
}
Then, you can fill in the vector as you would an array. Alternatively, as Nawaz points out, use reserve(), which reserves space for new elements, and/or push_back(), which adds elements onto the back, one at a time.
The class template std::vector is designed for this purpose.
class myClass {
private:
std::vector<int> myArray;
public:
myClass(int size);
someOtherMethod();
};
myClass::myClass(int size) : myArray(size)
{
}
I was wondering if it is possible to declare an array (size not known at this time), as a private member of a class and later set the size in the constructor of the class. For example:
class Test {
int a[];
public:
Test(int size);
};
Test::Test(int size) {
a[size]; // this is wrong, but what can i do here?
}
Is this possible or should I use dynamic arrays? Thanks!
Short Answer: No (The size of an array is defined at compile time only)
Long Answer:
You can use a vector to achieve the same result:
class Test
{
std::vector<int> a;
public:
Test(std::size_t size):
a(size)
{}
};
No this is not possible. Array declarations in headers must have constant sized value. Otherwise it's impossible for constructs like "sizeof" to function properly. You'll need to declare the array as a pointer type and use new[] in the constructor. Example.
class Test {
int *a;
public:
Test(int size) {
a = new int[size];
}
~Test() { delete [] a; }
private:
Test(const Test& other);
Test& operator=(const Test& other);
};
As other answers have pointed out, the size of an array is fixed at compile time. However, by using templates you can parameterise the size at compile time:
template <int N> class Test {
int a[N];
public:
Test() { }
};
Test<5> test;
Test<40> biggertest;
This technique does not let you compute the size at run time (as the dynamic std::vector solution does), but depending on your needs this may be sufficient.
First of all, it is generally better to initialize things in the initialization list of the constructor, not in the body of the constructor.
You can only initialize an array with a predefined bound if you know that bound at compile time. In this situation, you will need to dynamically allocate the space.
You must remember then to have a destructor that would delete the array when the object is destroyed or you would get a memory leak.
See Martin's solution (use std::vector), and remember that even if you need to pass a buffer to a C API std::vector lets you do it by passing &vec[0] :
std::vector<char> vec(10);
memset(&vec[0], 0, vec.size());
It's guaranteed to work, but only if the vector isn't empty (C++ quirks, <sigh>).
No, this is not possible. You should use a dynamic array such as an std::vector. C99 allows a struct to have an unsized array as the last member only, but even when you do this you still have to manually allocate the memory yourself, e.g. with malloc().
What you're talking about is not possible. Classes always have a constant size. You could have your class use a pointer to a dynamically allocated array or you could use a std::vector.