C++ Arrays, pointers, and the alternatives: struct wrappers and vectors – 8 on-the-fly snippets
Dive not that deep, but just enough to get the grasp!
Since there are a lot of newcomers who are looking for handy examples to obtain a good grasp for starting working with C++, here are 8 on-the-fly simple snippets with their short explanations. They are all around the fact that generally, in C++ we cannot return back from a function an array. Thus, we have to use other approaches that allow us to return a set of data back to the caller. Using pointers is a commonly used approach, however here, we also use, stt:array containers, structs, and vectors.
It is supposed that you have already started getting some familiarity with C++ and therefore you can quickly incorporate the snippets into your practice and learning process.
- 1. arr1 – Using a pointer function that returns a static array with fixed length
- 2. arr1a – Using a std::array structure in a function that returns a fixed size array
- 3. arr2 – Using a pointer function that returns a dynamic array
- 4. arr2a – Using a smart pointer in a function that returns a variable-length array
- 5. arr3 – Using a struct wrapper with a fixed-length array
- 6. arr3a – Using a struct wrapper with a pointer to an array and a custom constructor for setting up the size of the array
- 7. arr4 – Using a vector and a function that returns a vector of integers
- 8. arr4a – Using a vector and a function that returns a vector of strings – (actually the names of the 12 months – via the nl_langinfo constants: MON_1, MON_2, etc.)
arr1 – Using a pointer function that returns a static array with fixed length
A very simple example of returning a pointer to an integer array from a function.
As we have said, in C++, we cannot return an array from a function. A first commonly used approach is to return a pointer, pointing to the integer array, that the function returns. Here we use a local static variable for the array to be returned.
Using a local static array has the benefit of a static variable. This means that the lifetime of a static variable is throughout the scope it resides. Here, the scope is within the function. Thus there is no need to delete or free the array and its memory allocated.
However, the drawback is that we have to use a predefined, constant value for the fixed-size array.
arr1a – Using a std::array structure in a function that returns a fixed size array
In the previous example, we used a pointer function using a static array. Another option we have, instead of using a pointer-based approach, is to use the std::array container structure. And this is what this simple example here, is about.
The syntax for declaring and defining a std::array is quite simple:
where N is a constant value for the size of the array. For instance:
creates an integer array with fixed size of 10.
Note, that we also have to include the Memory Management Library:
Again, the drawback is that we have to use a predefined, constant value for the fixed-size array.
arr2 – Using a pointer function that returns a dynamic array
As we did in the first example, here we will also use a pointer. However, in this example, we use a dynamic array. This means that we dynamically allocate some memory from the heap, during the array creation. This is achieved by using the new keyword as int following syntax:
int* arr = new int[sz];
The length (the size) of the array, is passed as an argument to the function. Note that, after calling the function and getting the returned array, we have to clear it, e.g. by using the delete or free(), in order to avoid memory leaks. And this is actually, a drawback of this approach: we have to take care of clearing/deleting the dynamic memory allocated for the array.
arr2a – Using a smart pointer in a function that returns an array
In our previous examples arr1 and arr2 we have used raw/regular C/C++ pointers for an (integer) array. The array has been created locally in a function and returned back to the caller.
In arr1 we have used a static array, and thus we haven’t to think about deleting it since static objects leave within the scope they have been created (in our case inside the function). The drawback of this approach is that we can use only fixed-size arrays because they are static arrays.
In arr2 we have used a dynamic array which allows us to define its size. Actually, we have passed the desired size as a parameter to the function that creates it. The drawback, in this case, is that we have to take care and delete the memory allocated by the dynamic array (delete the array) in order to avoid any memory leaks.
In this example here, we are using a smart pointer.
A few words about smart pointers
Smart pointers have been incorporated in C++ standard library since C++ version 11 (Modern C++). They are class wrappers, and they can be considered as and/or “proxies” to the raw/regular pointers, that also have the ability to manage the lifetime of the object they are pointed to, e.g. an array. This means that we no longer have to worry about destroying the object (the array) of a smart pointer. The smart pointer itself takes care of this. When the object of the smart pointer is destroyed, then it frees the memory as well.
Basically, there are 3 types of smart pointers:
(The auto_ptr type has been deprecated).
In this example here, we use the unique_ptr type.
The syntax for declaring and defining a smart pointer array is pretty similar to the one of a raw/regular pointer. We use again the new keyword for allocating the necessary dynamic heap memory:
std::unique_ptr<T>; obj(new T);
std::unique_ptr<int> sp_arr(new int[sz]);
We also have to include the Memory Management Library:
arr3 – Using a struct wrapper with a fixed-length array
Another approach can be to use a struct wrapper for the array. So, instead of returning an array, we can use a function that returns the struct instance.
Short intro to C++ structures – struct
structs in C++ are classes that by default use public access modifiers for all of their members (variables, functions, etc). A struct is a user-defined data type that combines logically related data items of different data types like float, char, int, etc., together. Moreover, it cannot have null values.
A commonly used approach is to use structs as PODs (C Plain-Old-Data structures). A struct with no modifiers or methods is a C POD struct. This gives C++ a backward-compatible interface with C libraries. “A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor.” A struct instance is considered as a “struct variable”
However, the drawback in this example is that we use a fixed-size array. So, a better approach is to use a pointer in the struct, that allows us to set the desired size of the array. [See the next example snippet: arr3a]
arr3a – Using a struct wrapper with a pointer to an array and a custom constructor for setting up the size of the array
In our previous example snippet arr3 we used a struct with a predefine/fixed size of an array as a member variable of the struct.
Here we also use a struct as a wrapper of an array that can be returned from a function. However, this time we actually use a pointer to an array, the size of which can be set up via the struct’s instantiation. Structs, like classes, can also have constructors and de-constructors. And this is true in our example here. Our struct declaration includes a custom constructor, which is being used to set up the size of the member array, e.g.:
Moreover, the struct’s declaration also uses a de-constructor, in order to free the memory allocated by the array upon the struct instance goes out of scope, and thus, it prevents any memory leaks. Finally, please note, that we also use a predefined value for the size of the array that can be used when instantiating the struct using the default constructor. e.g.:
arr4 – Using a vector and a function that returns a vector of integers
A few words about vectors
Using a vector might be the most preferable approach when you want to return a set of values. This is because vectors act similarly to array structures (LIFO sequence containers), and they are very handy to be resized (adding, and deleting items). Moreover, the destruction of a vector is automatic once the vector goes out of scope, and the allocated memory is free back on the heap.
Vectors are based on the std::vector class template that contains the vector container and its member functions. It is defined inside the header file. Declaring, defining, and using vectors is very easy. See the code.
arr4a – Using a vector and a function that returns a vector of strings – (actually the names of the 12 months)
Another simple example that uses and returns a vector -this time a vector of strings. In this example, instead of using string literals as values for the elements of our vector, we are going to use the names of months. The standard names of the 12 months of the year are obtained via the nl_langinfo constants (MON_1, MON_2, etc).
You can find here the repo with all snippets!
That’s it for now! I hope you enjoyed it!
Thanks for reading and stay tuned!