Initializing an array in C++
16 June, 2010 § 6 Comments
Most people know about member initialization lists in C++. Initialization lists allow you to initialize a variable when the class is instantiated, versus assigning a value within the constructor.
Have you ever tried to initialize an array? Many websites will tell you that this is not possible. In fact, if you try this you’ll get a compiler error:
class array { public: bool vars[2]; array() : vars[0](true), vars[1](true) {} };
BUT, if all you want to do is zero-initialize the array then you’re in luck! Simply place parens after the array and the array will be zero-initialized. Try out the code below:
#include <iostream> class arrayInit { public: bool vars[2]; arrayInit() : vars() {} }; class array { public: bool vars[2]; array() {} }; int main() { arrayInit a; std::cout << "a.vars[0] = " << a.vars[0] << std::endl; std::cout << "a.vars[1] = " << a.vars[1] << std::endl; array b; std::cout << "b.vars[0] = " << b.vars[0] << std::endl; std::cout << "b.vars[1] = " << b.vars[1] << std::endl; }
Using code like this in Visual Studio can generate warning C4351 because the behavior is different from previous versions of VC++. Here is the documentation from MSDN:
Compiler Warning (level 1) C4351
new behavior: elements of array ‘array’ will be default initialized
When an array is in a constructor’s member initialization list, the elements of the array will be default initialized. In previous versions of Visual C++, when an array was in a constructor’s member initialization list, the elements of the array may not have been default initialized in some cases.
If the array’s element type does not have a constructor, the elements of the array will be initialized with the corresponding zero representation for that type.
C4351 means that you should inspect your code. If you want the compiler’s previous behavior, remove the array from the constructor’s member initialization list.
If you want the new behavior, which is likely, because the array was explicitly added to the constructor’s member initialization list, use the warning pragma to disable the warning. The new behavior should be fine for most users.
One situation where the new behavior can result in unexpected behavior is when placement new is used to construct the object that has the array as a member, and the program depends on the contents that the memory (for the elements of the default initialized array) had before the call to placement new. In this case, the older compiler would have left the contents of memory unchanged, but the new behavior will cause default initialization of the array elements, overwriting the original contents in memory.
Hey Jared!
This solution is a little obscure but it might accomplish what you are trying to do:
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#define CONSTRUCTOR_ELEMENTS(z, n, text) BOOST_PP_COMMA_IF(n) int a ## n
#define INITIALIZER_ELEMENTS(z, n, text) BOOST_PP_COMMA_IF(n) m_a ## n(a ## n)
#define ITEMS(z, v, _) \
BOOST_PP_CAT(int m_a, v);
#define ELEMENTS(n) BOOST_PP_REPEAT(n, ITEMS, ~)
#define INITIALIZABLE_ARRAY(name, size) \
struct Sub \
{ \
Sub(BOOST_PP_REPEAT(size, CONSTRUCTOR_ELEMENTS, ~) ) \
: BOOST_PP_REPEAT(size, INITIALIZER_ELEMENTS, ~) \
{} \
int& operator[](int nIndex){return *(&m_a0 + nIndex); } \
protected: \
ELEMENTS(size) \
} name; \
/**/
#include <iostream>
using namespace std;
class A
{
public:
A():m_nArray(7,4,3){}
INITIALIZABLE_ARRAY(m_nArray, 3);
};
int main()
{
A a;
for(int i=0; i<3; i++)
cout << a.m_nArray[i]<< endl;
return 0;
}
Basically what it does is not be an array but just multiple member variables. But you wouldn’t know using it. In the meantime I’ll come up with another solution 🙂
Quite long-winded there for some initialization 🙂
Yeah, one of the ways to solve this problem if you don’t want default-initialization would be to create a class that has encapsulates a data type and initializes it that way (essentially the solution you’ve presented there). Neat tricks though with Boost and overloading the comma operator 😉
Of course I’ll just rely on default-initialization until there comes a time when it is otherwise necessary.
Yeah I’d like to do a benchmark test and see if it actually would be faster than just a memcpy or assignment in the constructor. It is a trick as you pointed out; the preprocessor stuff from Boost allowed me to create a class with n-elements and the subscript operator allows you to access the value of any index. But C++ should allow array member-variable initialization.
In C++0x (I guess should start calling it C++11?) from Wikipedia article (http://en.wikipedia.org/wiki/C%2B%2B0x) says we can do something like this:
class SomeClass {
public:
SomeClass() {}
explicit SomeClass(int new_value) : value(new_value) {}
private:
int value = 5;
};
It is a little different; but I wonder if I’ll also be able to change that member variable to look like this:
int m_value[] = {1,2,3};
If so that would be your fix! If you cannot do that; I came up with another option using a union and union’s can have a constructor of which I’d initialize the array that way! 🙂
Haha yeah that would work. Have you tested it out with the array member?
I’m curious to see how crazy your union solution would look 🙂
Hey Hey Jared! It actually didn’t compile 😦 Here is what I had:
class A
{
public:
A() : m_elements(1,2,3)
{
}
union U
{
U( int a1, int a2, int a3 ) : S(a1, a2, a3){}
struct S
{
S(int a1, int a2, int a3): m_a1(a1), m_a2(a2), m_a3(a3){}
int m_a1, m_a2, m_a3;
};
int m_arr[3];
} m_elements;
};
int main()
{
A a;
int nElemnent = a.m_elements.m_arr[2];
}
Now I know you’re thinking that is pretty complicated; with preprocessor macros it could simply become: INITIALIZEABLE_ARRAY(m_elements, 3) or something like that.
But I still haven’t tested out the array initializer member variable with C++0x:
int m_value[] = {1,2,3};
I will though! Soon! 🙂