Question about header files and adding additional cpp source files.

Pages: 12
Declarations.hpp
 
class User_Data_Class;


Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User_Data_Class{
	private:
		std::string Name;
		std::string Pass;
		std::string Access;

	public:
		User_Data_Class(std::string n, std::string p, std::string a) : Name(n), Pass(p), Access(a){};

		std::string Get_Name(){return Name;}
		std::string Get_Pass(){return Pass;}
		std::string Get_Access(){return Access;}

};


I backed up my program and started reworking it into different files based on what was mentioned here.

In the code above, I declared the class in the .hpp and have the definition in the main .cpp file. Is this right / good? Should I just have the whole definition in the .hpp?

I don't think this class is needed across all .cpp files. Only a few functions need it. Maybe I should put it in its own separate .hpp file and include it only where it's needed?



Forward declarations of classes (your first code) are for a different purpose that you don't need to worry about just yet.

If you call any of the functions within that class, or use any of the members ("fields") like Name or Pass, then the caller needs to know the full definition of the class.

Generally, you would put your class definition (your second code) inside the header file. Your functions like Get_Name are implicitly inline because you are defining them within the class definition. This is fine, and a matter of choice.

If you have another file that needs to create or use a User_Data_Class object, it needs to know the definition of the class, and so it needs to #include the header file that contains the definition of the class.
Last edited on
Here's a relatively simple 4-file project that shows some basic file separation.

UserData.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef USER_DATA_HPP
#define USER_DATA_HPP

#include <string> // lets the class create a string object

// Only Name, for brevity
class User_Data {
  private:
	std::string Name;

  public:
	User_Data(const std::string& name)
	: Name(name) {};

	std::string Get_Name(){return Name;} //note: defined inline within class definition
};

#endif 


Stuff.hpp
1
2
3
4
5
6
7
8
9
10
11
#ifndef STUFF_HPP
#define STUFF_HPP

#include <string>

// declares function; does not define.
// you could also define it as inline in the header
// it's a matter of taste
void do_stuff(const std::string& str);

#endif 


Stuff.cpp
1
2
3
4
5
6
7
#include <string>
#include <iostream>

void do_stuff(const std::string& str)
{
	std::cout << "Hello, " << str << "!\n";
}


main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "UserData.hpp" // lets user make a User_Data object
#include "Stuff.hpp" // lets user call "do_stuff"

#include <string> // lets user make a std::string object
#include <iostream> // lets user use std::cout object

int main()
{
	std::cout << "Enter name: ";
	std::string name;
	std::cin >> name;
	
	User_Data data(name);
	
	do_stuff(data.Get_Name());
}


Output:
Enter name: Civilization
Hello, Civilization!


Note that since Stuff.cpp never needs to use a UserData object, it doesn't need to #include that header.

If you added some other file for something like database functionality, if that functionality needed to also use a UserData object, you would #include it there as well.
Last edited on

User_Data.hpp

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
// this code will only appear once in a translation unit
// useful if this header is included in multiple files
// not standard but widely used by nearly all the compilers
#pragma once 

//header guards, doesn't hurt to have this as well as pragma once

#ifndef  User_Data.hpp
#define User_Data.hpp
                                  
class User_Data{ // don't append "class" to every class name
	private:
		std::string Name;
		std::string Pass;  // what is Pass? if it is a password then name it so
		std::string Access;

	public:
		User_Data(const std::string& n, // const ref, we are not changing these values, reference more efficient
                          const std::string& p, // format like this, 1 parameter per line, easier to read
                          const std::string& a) 
                  : Name(n), Pass(p), Access(a){}; // could do validation inside braces, or use a validation class

                // inline is implicit because function are fully defined here
                // inline means 
                // https://en.cppreference.com/w/cpp/language/inline
               
                // these 3 functions could go into an interface class named IUser_Data
                // this class would publicly inherit from IUser_Data
                [[nodiscard ]] // error if the value returned is not assigned to or used for anything, part of c++17
		std::string Get_Name() const {return Name;} // const because not changing any member values
                 [[nodiscard ]]
		std::string Get_Pass() const {return Pass;}
                 [[nodiscard ]]
		std::string Get_Access() const {return Access;}

// to do  overload ostream<< to print out data

};
#endif // end of header guard 


main.cpp

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

#include "User_Data.hpp"

int main() {
   // create object
   User_Data MyUser("Fred Bloggs", "me", "Highly Protected");

// to do  print out data

}


Compile:

Release version:

g++ main.cpp -std=c++17 -Wall -Wextra -pedantic-errors -O3 -o UserData.exe


Debug

g++ main.cpp -std=c++17 -Wall -Wextra -pedantic-errors -g -O1 -o UserData.exe

Last edited on
Thanks for all the replies. I have to research some of what was mentioned here.

I actually read the Classed I and Classes II here on this site. It was very helpful. I also read up on using the STLs as well.

class User_Data{ // don't append "class" to every class name


There's a reason why I appended the "_Class" to the end of that class name. I need to name it something unique because shortly after defining that class I wrote this to establish this type:
using User_Data = vector <User_Data_Class>;

All of the users are saved to a vector named "User". I should probably change is to "Users" now. Anyways, I created the type def User_Data because I thought I would be using that type def numerous times to iterate through the vector. I have to think all this through again. This is the third time I'm rewriting this program to make it better. It is a learning experience.
@CVRIV

Naming variables and function well is an important thing. Ideally, the code should tell a story of what it does, simply by naming functions and variables well.

I have to think all this through again. This is the third time I'm rewriting this program to make it better. It is a learning experience.


As mentioned earlier, make sure you have the design right first, otherwise you may be rewriting over and over ....
This is very important, there may be things you don't even know about yet - hopefully we can point them out at the beginning.

Tell us about what you program wants to achieve:
Is it an assignment for school? Post the whole thing verbatim;
the classes you think you need with their purpose and / or responsibilities;
the functions with a description of what they do, if not obvious from it's name - no implementation code at this point;
Any sample input and output data;
Any pseudo code you already have;
Any thing else you might think is useful.

You could post a mixture of class definitions and pseudo code - whatever is easiest for you.
It's not an assignment for school. I'm literally doing it just for learning purposes. I can only plan so much out in advance due to my lack on C++ knowledge. I know a lot of stuff, but there's so much I just don't know. Also, the stuff I do know is kind of jumbled in my head. I constantly have to reference stuff to remember.

I'm going to code all this again based on what I learned here and then I'll come back and show what I did. Coding and recoding is really a bad thing when learning, because it allows me to see the faults.
CVRIV wrote:
it's getting annoyingly large in size where im having to scroll through lines and lines of code to get anywhere.

I was under impression that some editors are able to show "table of contents"/"index" that makes it easier to jump to code of specific function, etc.
(I'm quite sure it was possible in previous millennium ...)


A core concept is "translation unit". One (cpp-file) is compiled into one object file.
A project/program can have multiple translation units, i.e. multiple cpp-files that are compiled separately.
Linker combines the object files into executable binary of the program.

A translation unit is not "just" the cpp-file. It has also all the content that the preprocessor includes. Content from the header files.


Second important point is ODR, One Definition Rule. Objects can be defined only once among all the object files that are linked together.

Implementation of a function and global variable are such "objects". If you define them in header and include that header in more than one cpp-file, then more than one translation unit will contain that definition, which violates ODR.

Note though that class definition is not such thing. Description of a type is not object code. It is just instruction for compiler for generating object code for object instances of that type.

In other words, headers have what multiple cpp-files need to know. Knowledge that they can share.


Another peculiarity are the templates. Template for implementation of a function is not "object" either. It is ok to instantiate a concrete function with same type in multiple translation units. Linker knows how to avoid that multiple definition.
Thanks for all the info everyone. My program is starting to look much nicer now. I don't want to try to much at one time. The next time I right something I will add some more common practices when coding.

I added numerous source files to help separate my code into manageable chunks. I created a folder for all of these specific source files too.

Each of these source files has one of my header files included in code. The header file is in the root directory of the project so I obviously got a error message saying the header doesn't exist. I remember from learning DOS a looooong time ago that I could ../ to go back a directory. That worked but is that the right way? Isnt that like a relative something something? I'd like to use static pathways. I forgot the terminology.
Often one places the source files (.cpp files) in a separate directory under the project (say, src) and compile the files from the project directory; the headers are in their own directory (say, include).
Then, we could write #include "include/my_header.h"

For external libraries, which are not part of your code base, add the directory containing headers to the compiler's include path (if not already in the include path).
Last edited on
Right but even if I had all the sources in one directory and the includes in another directory, I would still have to use the ../ to get to the includes from the source directory.
You do have option B:
add the directory containing headers to the compiler's include path
> add the directory containing headers to the compiler's include path


I'm not in favour of doing this for finding the projects own header files, unless all includes are prefixed with a project specific name. The problem is primarily with headers with common names (say util.h); the risk is that an unrelated header with the same name coulld be picked up from somewhere else .
#include <my_project/util.h> with the directory containing the folder my_project in the include path is safer.


A proposal for a canonical project structure for new C++ projects submitted to SG15:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1204r0.html#src-dir
Topic archived. No new replies allowed.
Pages: 12