Declare array in C++ header and define it in cpp file?

72,741

Solution 1

Add extern to the declaration in the header file.

extern int lettersArr[26];

(Also, unless you plan to change the array, consider also adding const.)

The definition must have a type. Add int (or const int):

int lettersArr[26] = { letA, /*...*/ };

Solution 2

Other have described how the array initialization can be moved to an implementation file, which is not exactly answering your question, but is a workaround that's useful to know.

I just want to declare an array in my C++ header file

If you really want to have the array all in your header file, including having the initialization in your header file, then you can

  • give it internal linkage by using static, or

  • use a local static in an inline function (which supports effectively external linkage), or

  • use a little template trick (also supports external linkage).

The last two solutions are workarounds for the lack of "inline" data in C++. That is, the ability to define the same namespace scope object in more than one translation unit. You have that for functions, via inline, but unfortunately not for objects: without employing some workaround the linker will just protest about multiple definitions.

Internal linkage

This is generally not a good solution. It creates one array in each translation unit where the header is included. But it's preferable for relatively small const objects because it's so simple:

#include <stddef.h>
#include <iostream>

int const   letA    = 'A';
int const   letB    = 'B';
int const   letC    = 'C';
int const   letD    = 'D';
int const   letE    = 'E';
int const   letF    = 'F';
int const   letG    = 'G';
int const   letH    = 'H';
int const   letI    = 'I';
int const   letJ    = 'J';
int const   letK    = 'K';
int const   letL    = 'L';
int const   letM    = 'M';
int const   letN    = 'N';
int const   letO    = 'O';
int const   letP    = 'P';
int const   letQ    = 'Q';
int const   letR    = 'R';
int const   letS    = 'S';
int const   letT    = 'T';
int const   letU    = 'U';
int const   letV    = 'V';
int const   letW    = 'W';
int const   letX    = 'X';
int const   letY    = 'Y';
int const   letZ    = 'Z';

static int lettersArr[26]   =
{
    letA, letB, letC, letD, letE, letF, letG, letH,
    letI, letJ, letK, letL, letM, letN, letO, letP, letQ, letR, letS,
    letT, letU, letV, letW, letX, letY, letZ
};

int main()
{
    using namespace std;
    for( int i = 0;  i < 26;  ++i )
    {
        cout << char( lettersArr[i] );
    }
    cout << endl;
}

Local static in inline function

This is probably the generally "best" solution, to use when there is no overriding reason for choosing one of the other solutions. One nice thing is that it's easy to provide dynamic initialization. Here I've just assumed that you will never store 0 in the array (add some extra checking logic if that assumption doesn't hold):

#include <stddef.h>
#include <iostream>

template< class Type, int n >
int countOf( Type (&)[n] ) { return n; }

typedef int LettersArray[26];

inline LettersArray& lettersArrayRef()
{
    static LettersArray theArray;

    if( theArray[0] == 0 )
    {
        // Assuming normal ASCII-based character set with contiguous alpha.
        for( int i = 0;  i < countOf( theArray );  ++i )
        {
            theArray[i] = i + 'A';
        }
    }
    return theArray;
}

static LettersArray&    lettersArr  = lettersArrayRef();

int main()
{
    using namespace std;
    for( int i = 0;  i < 26;  ++i )
    {
        cout << char( lettersArr[i] );
    }
    cout << endl;
}

Template trick

The template trick works because the standard's ODR, One Definition Rule, makes a special exemption for templates:

#include <stddef.h>
#include <iostream>

int const   letA    = 'A';
int const   letB    = 'B';
int const   letC    = 'C';
int const   letD    = 'D';
int const   letE    = 'E';
int const   letF    = 'F';
int const   letG    = 'G';
int const   letH    = 'H';
int const   letI    = 'I';
int const   letJ    = 'J';
int const   letK    = 'K';
int const   letL    = 'L';
int const   letM    = 'M';
int const   letN    = 'N';
int const   letO    = 'O';
int const   letP    = 'P';
int const   letQ    = 'Q';
int const   letR    = 'R';
int const   letS    = 'S';
int const   letT    = 'T';
int const   letU    = 'U';
int const   letV    = 'V';
int const   letW    = 'W';
int const   letX    = 'X';
int const   letY    = 'Y';
int const   letZ    = 'Z';

template< class Dummy >
struct Letters_
{
    static int  array[26];
};

template< class Dummy >
int Letters_< Dummy >::array[26]    =
{
    letA, letB, letC, letD, letE, letF, letG, letH,
    letI, letJ, letK, letL, letM, letN, letO, letP, letQ, letR, letS,
    letT, letU, letV, letW, letX, letY, letZ
};

static int (&lettersArr)[26]    = Letters_<void>::array;

int main()
{
    using namespace std;
    for( int i = 0;  i < 26;  ++i )
    {
        cout << char( lettersArr[i] );
    }
    cout << endl;
}

Cheers & hth.,

Solution 3

Change what you have in the header to:

extern int lettersArr[26];

so that it will become a declaration, not a definition.

Solution 4

Here's a snippet from one of my header files (the implementation .cpp file accesses the array): (Use dummy::messages outside of the dummy namespace to access the array.)

<pre>
    namespace dummy {

const static string messages[] = {
        "Unix does not echo the password field. Why do you think this is?",
        "The firewall blocks external access to ouranos. You need to login to helios and ssh or sftp to ouranos",
        "You need to experience of the command line. Not all systems have a gui.",
};

class Message {
public:
    Message();
    virtual ~Message();

    string getMessage();
    string getMessage( int index );
    int getRandomNumber();
};

} /* namespace dummy */
</pre>
Share:
72,741
tree-hacker
Author by

tree-hacker

Updated on September 20, 2020

Comments

  • tree-hacker
    tree-hacker almost 4 years

    This is probably a really simple thing but I'm new to C++ so need help.

    I just want to declare an array in my C++ header file like:

    int lettersArr[26];
    

    and then define it in a function in the cpp file like:

        lettersArr[26] = { letA, letB, letC, letD, letE, letF, letG, letH,
            letI, letJ, letK, letL, letM, letN, letO, letP, letQ, letR, letS,
            letT, letU, letV, letW, letX, letY, letZ };
    

    but this doesn't work.

    Have I got the syntax wrong or something? What is the correct way to to this?

    Thanks a lot.

  • tree-hacker
    tree-hacker over 13 years
    I get the error "storage class specified for 'lettersArr'" in the hpp file when I use that. Do you know why? Thanks
  • tree-hacker
    tree-hacker over 13 years
    I get the error "storage class specified for 'lettersArr'" in the hpp file when I use that. Do you know why? Thanks
  • tree-hacker
    tree-hacker over 13 years
    I get the error "storage class specified for 'lettersArr'" in the hpp file when I use that. Do you know why? Thanks
  • aschepler
    aschepler over 13 years
    @tree-hacker: Is that declaration within a class or something?
  • Kos
    Kos over 13 years
    Compiles for me. Is it the only error you get? What's your compiler?
  • tree-hacker
    tree-hacker over 13 years
    Yes it is within a class in the header file like class Letters {private: /* the declaration*/ ...}. Where should it be?
  • Kos
    Kos over 13 years
    That changes a lot! Say it at the beginning. If it's inside the class definition, then it's a part of class description, not an declaration of an external global variable - totally unrelated concepts! If it's part of a class, then add it without extern to the class definition and then initialize it in the constructor.
  • aschepler
    aschepler over 13 years
    If you want every Letters object to have its own variable lettersArr, then put it back the way it was, and fix class constructors of Letters to initialize the array. If you want all Letters objects to share a single lettersArr array, make it a static member.
  • tree-hacker
    tree-hacker over 13 years
    @Kos That's what I thought I did. The first bit of code I gave was inside the class definition in the header and the second was inside the constructor but it doesn't work for some reason?
  • aschepler
    aschepler over 13 years
    You can't use = to assign an array inside a constructor. Instead you could std::copy from some other const int array[26].