Getting duplicate definitions even when using header guards

I've got an application, and I've been trying to move related code off to separated .cpp files, and link things via .h files.

Because the code all ties to things shared in the main .h file, I have included that in pertinent places.

Well, in spite of having used header guards everywhere, I've now got 16 duplicate definition errors (linker messages??), and all but one of them are defined in the main .h file.

What are the situations where #ifndef/#endif does not help? Looks like I'll need to move my separated code back into the main file, cluttering things up immensely.

I'm trying to find stuff online, but usually it's because the person did not use #ifndef. A lot of my array definitions, and maybe all of them, are making up the bulk of the error messages.

I'm subsequently reading that header guards are to prevent multiple definitions within a single CPP, but if I include a .h across multiple CPP's, I'm not going to be helped by the header guards.

Since these structures and arrays are referenced in most CPP's (which is why I put them in a .h file) what guidelines can I follow to get me out of this mess?

I'm using VC++, if that makes any difference.
Last edited on
Hello zydeholic,

With out seeing your files it sounds like you are trying to the wrong parts in header files. Although this may seem like a good idea it does not always work.

My experience has been that header files are best used to define "classes", "structs" and "prototypes", but when you start putting header files like "iostream"and "string" into header and trying to use this to start a ".cpp" file it is usually a problem.

Post your code and give everyone a better idea of what you are trying to do.

Hope that helps,

Andy
I've moved things back to how they were before I messed things up. Here is some expunged contents of the header file that seemed to be the offending agent. I can't show the rest of the code, for business reasons. Same reasons I was reluctant to post code before. Sorry for being so top secret about it.

I have a Structures.h file that only contains structs of strings, and I had no problem adding that to several other files with header guards.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#pragma once


#include "pch.h"

#ifndef MATCHER_ORIGINAL_H
#define MATCHER_ORIGINAL_H

#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#include <stdlib.h>
#include "File_Reader.h"
#include "Structures.h"



using namespace std;
//


//string * cardinal_ary;

string cardinal_ary[24] = { "E","East", "ENE",  "ESE",  "N", "NE", "NNE", "NNW", "North", "Northeast", "NW", "S", "SE", "South",\
								"Southeast", "Southwest", "SSE", "SSW", "SW", "W","West",  "WNW", "WSW" };


string * to_parse;

string type_ary[20] = { "Av", "Ave", "Avenue","Blvd", "Boulevard", "Cir", "Circle","Ct",\
							"Court",  "Highway", "HW", "HWY", "Ln", "Lane",  "Rd", "Road", "St", "Street", "Wy", "Way" };


string states_ary[5][2] = { {"Alabama", "AL"}, \
{"Alaska", "AK"}, \
{"Arizona", "AZ"}, \
{"Arkansas", "AR"}, \
{"California", "CA"},\
};



// global variables used throughout,
vector <string> words;
int word_ptr;
bool rslt;

//function protypes
//general, maybe not in class
bool isnumeric(string);
bool parse_beginning_numeric_and_unit(string, string&, string&);
//string get_2D_value(string **, int, int, int);


//move to parent class

bool found_in_array(string, const string*, int);
bool found_in_array2d(string, string**, int);

string pop_first_word(string&, const string);
bool my_replace_string(string&, string, string);
string my_toupper(string);
string preprocess_string(string, string);
bool words_to_vector(string, vector <string>&, string);
bool print_out_structure(address_struct);

//bool words_to_vector(string, string&, string);

//move to regular address class
bool fill_in_regular_address(address_struct&, vector<string>, int&);

//move to PO class
bool fill_in_po_address(address_struct &, vector<string>, int &);

// move to hwy class
bool fill_in_hwy_address(address_struct &, vector<string>, int &);

results_struct results;
bool empty_words(vector <string>);


#endif 
Defining types (including classes/structs) is fine in a header file included in multiple files.

Declaring functions is is fine in a header file included in multiple files.

However, declaring actual variables and objects is problematic, because if you declare them at global scope, and then try and declare them in more than one file, you're defining the same thing in the same scope (global scope) multiple times.

Thus, lines 25 - 48 are causing the problem. You are defining each of those global variables multiple times.

There are ways to deal with this:

1) Don't use global variables. They can very easily cause problems in even moderately small-sized programs, making errors hard to find. They seem convenient at first, but they're usually a bad idea.

2) For constants, define them as const. Constants at the global scope have internal linkage only, so when you define them in a header, they're not global variables, and there will be no multiple definitions. For example, lines 25 - 40 can all happily be const.

3) Don't use global variables. No, seriously. Learn how to pass relevant data around your program in a controlled, contained manner. Even if it means a bit of extra typing.

4) For those very rare cases where you truly do need global variables, you define them once, and once only, in a single translation unit. Everywhere else (i.e. in your header) you declare them as extern.

5) Don't use global variables. In case you hadn't picked up on that by now.

---

Other issues with your code:

1) Why do you have #pragma once as well as multiple inclusion guards? That's redundant. Do one or the other, but having both adds nothing except clutter.

2) Why is some of the header code outside of the multiple inclusion guard?

3) Don't put using statements in headers. Particularly, don't put using namespace statements in headers. Particularly, don't put using namespace std; statements into headers. Think about what you're doing - you're forcing every single source file that uses that header, to pull in the entirety of the std namespace into the global namespace, regardless of whether that's wanted - or even dangerous - to the source file that's including it. Don't do it.

Now, as for:

Handy Andy wrote:
when you start putting header files like "iostream"and "string" into header and trying to use this to start a ".cpp" file it is usually a problem.


This, like so much of the "advice" Handy Andy gives, is utter nonsense. There's nothing wrong with including headers in other headers. In fact, if the code inside a header requires another header to be included, then it's exactly the right thing to do.

Apply extreme caution when reading Handy Andy's posts, as you are likely to "learn" things that aren't true.
Last edited on
Many thinks Mikey. Lots for me to chew on, and get my act together. Yes, the globals are there because (as I'm posting in the beginners section) I was having a hard time passing them down by reference.

#pragma once - that's system generated. And didn't seem to be working, so I decided to get proactive. Now I know what the problems are.

VC++ threw me an error for not putting #included "pch.h" outside of the header guard. It did it in one place, so I moved them all out of the guard. the PCH.H code is system generated. I just moved it.

I think I"m going to print off your response here and put it in a folder. Maybe the title on the folder will be "DON"T USE GLOBAL VARIABLES". Not sure where I got that from!!! Thanks again, big time. Seriously, I'm going to print it off for reference.
#pragma once causes the compiler to read the file once when compiling a single compilation unit (.cpp file). But if you have multiple .cpp files that #include the file, it will include the file's contents in each of the .cpp's corresponding .o files.

Keep in mind here the difference between declaring a variable and defining it. A declaration tells the compiler that a variable exists. A definition actually allocates memory for it.
1
2
extern int i;   // this is a declaration
int i;    // this is a definition. 

A program can contain any number of declarations of a variable but must contain exactly one definition. So to fix your problem, move lines 25-40 in main.h to main.cpp. Replace the lines in main.h with:
1
2
3
4
extern string cardinal_ary[24];
extern string * to_parse;
extern string type_ary[20];
extern string states_ary[5][2];

Last edited on
Thank you dhayden.
@zydeholic You're welcome - glad I could help!
Topic archived. No new replies allowed.